keri-ts 2.2.4 → 2.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/esm/keri/npm/src/_dnt.polyfills.js +14 -14
- package/esm/keri/npm/src/acdc/dhking.js +29 -2
- package/esm/keri/npm/src/acdc/observing.js +10 -3
- package/esm/keri/npm/src/acdc/query-resend.js +74 -0
- package/esm/keri/npm/src/acdc/regengine.js +39 -9
- package/esm/keri/npm/src/acdc/regeventing.js +5 -3
- package/esm/keri/npm/src/acdc/registering.js +19 -0
- package/esm/keri/npm/src/acdc/salting.js +4 -0
- package/esm/keri/npm/src/acdc/unblinding.js +38 -3
- package/esm/keri/npm/src/app/bootstrap/authenticating.js +32 -10
- package/esm/keri/npm/src/app/bootstrap/oobiery.js +363 -30
- package/esm/keri/npm/src/app/credentials/credential-importing.js +33 -4
- package/esm/keri/npm/src/app/credentials/root-replicator.js +27 -15
- package/esm/keri/npm/src/app/credentials/schema-resolving.js +5 -1
- package/esm/keri/npm/src/app/credentials/tel-publishing.js +16 -7
- package/esm/keri/npm/src/app/delivery/forwarding.js +205 -44
- package/esm/keri/npm/src/app/delivery/relaying.js +232 -0
- package/esm/keri/npm/src/app/delivery/respondant.js +8 -5
- package/esm/keri/npm/src/app/exchanges/exchange-messaging.js +1 -1
- package/esm/keri/npm/src/app/exchanges/exchanging.js +65 -9
- package/esm/keri/npm/src/app/identity/delegating.js +66 -9
- package/esm/keri/npm/src/app/identity/habbing.js +190 -71
- package/esm/keri/npm/src/app/identity/keeping.js +111 -39
- package/esm/keri/npm/src/app/identity/retire-identifier.js +98 -0
- package/esm/keri/npm/src/app/ipex/disclosure-replaying.js +116 -0
- package/esm/keri/npm/src/app/ipex/group-ipex.js +122 -25
- package/esm/keri/npm/src/app/ipex/group-issuance.js +137 -37
- package/esm/keri/npm/src/app/ipex/ipex-discloser.js +94 -31
- package/esm/keri/npm/src/app/ipex/ipex-grouping.js +53 -3
- package/esm/keri/npm/src/app/ipex/ipex-v2.js +7 -2
- package/esm/keri/npm/src/app/ipex/issuance.js +147 -44
- package/esm/keri/npm/src/app/ipex/pending-presentations.js +4 -2
- package/esm/keri/npm/src/app/ipex/presenting.js +3 -18
- package/esm/keri/npm/src/app/ipex/receive-issuance.js +2 -1
- package/esm/keri/npm/src/app/ipex/unblinding-workflow.js +39 -11
- package/esm/keri/npm/src/app/mailbox/agent-inbox.js +292 -0
- package/esm/keri/npm/src/app/mailbox/forward-handler.js +298 -52
- package/esm/keri/npm/src/app/mailbox/kel-publisher.js +26 -31
- package/esm/keri/npm/src/app/mailbox/mailbox-poller.js +70 -62
- package/esm/keri/npm/src/app/mailbox/mailboxing.js +20 -4
- package/esm/keri/npm/src/app/multisig/counseling.js +71 -27
- package/esm/keri/npm/src/app/multisig/group-members.js +41 -5
- package/esm/keri/npm/src/app/multisig/grouping.js +50 -2
- package/esm/keri/npm/src/app/multisig/multisig-workflows.js +3 -18
- package/esm/keri/npm/src/app/multisig/proposing.js +1 -16
- package/esm/keri/npm/src/app/multisig/rejoin-group.js +99 -0
- package/esm/keri/npm/src/app/multisig/replace-member.js +29 -8
- package/esm/keri/npm/src/app/protocol/parsering.js +45 -17
- package/esm/keri/npm/src/app/protocol/protocol-processing.js +47 -21
- package/esm/keri/npm/src/app/queries/query-replies.js +0 -22
- package/esm/keri/npm/src/app/queries/relay-refresh.js +81 -0
- package/esm/keri/npm/src/app/queries/witness-inquisitor.js +180 -34
- package/esm/keri/npm/src/app/runtime/agent-runtime.js +42 -73
- package/esm/keri/npm/src/app/runtime/cue-runtime.js +45 -5
- package/esm/keri/npm/src/app/runtime/ipex-auxiliaries.js +8 -1
- package/esm/keri/npm/src/app/runtime/operation-owners.js +26 -56
- package/esm/keri/npm/src/app/runtime/workflow-contract.js +7 -4
- package/esm/keri/npm/src/app/transport/http-client.js +20 -2
- package/esm/keri/npm/src/app/transport/message-source.js +2 -1
- package/esm/keri/npm/src/app/transport/messengering.js +39 -8
- package/esm/keri/npm/src/app/witness/ingress.js +7 -0
- package/esm/keri/npm/src/app/witness/receipt-client.js +43 -16
- package/esm/keri/npm/src/app/witness/receipting.js +62 -20
- package/esm/keri/npm/src/cli/messaging/multisig.js +4 -3
- package/esm/keri/npm/src/core/errors.js +73 -0
- package/esm/keri/npm/src/core/kel/delegation-ancestry.js +11 -2
- package/esm/keri/npm/src/core/kel/eventing.js +25 -28
- package/esm/keri/npm/src/core/kel/kever.js +15 -9
- package/esm/keri/npm/src/core/mailbox/kram-policy.js +16 -0
- package/esm/keri/npm/src/core/mailbox/kram-pruning.js +25 -6
- package/esm/keri/npm/src/core/mailbox/kram-time.js +34 -6
- package/esm/keri/npm/src/core/mailbox/kraming.js +58 -2
- package/esm/keri/npm/src/core/missing-anchor-query.js +20 -0
- package/esm/keri/npm/src/core/routing.js +18 -25
- package/esm/keri/npm/src/core/scheming.js +12 -5
- package/esm/keri/npm/src/core/wire-fields.js +126 -0
- package/esm/keri/npm/src/db/basing.js +4 -13
- package/esm/keri/npm/src/db/core/indexeddb-persistence.js +3 -2
- package/esm/keri/npm/src/db/core/webdber.js +85 -27
- package/esm/keri/npm/src/db/mailbox/mailboxing.js +13 -0
- package/esm/keri/npm/src/db/subing.js +4 -0
- package/esm/keri/npm/src/runtime/index.js +11 -5
- package/esm/keri/npm/src/time/mod.js +5 -0
- package/esm/keri/npm/src/vdr/credential-tsn-record.js +2 -7
- package/esm/keri/npm/src/vdr/eventing.js +71 -28
- package/esm/keri/npm/src/vdr/registry-tsn-record.js +3 -13
- package/esm/keri/npm/src/version.js +2 -2
- package/package.json +1 -1
- package/types/_dnt.polyfills.d.ts +9 -9
- package/types/_dnt.polyfills.d.ts.map +1 -1
- package/types/acdc/dhking.d.ts +8 -0
- package/types/acdc/dhking.d.ts.map +1 -1
- package/types/acdc/observing.d.ts +5 -1
- package/types/acdc/observing.d.ts.map +1 -1
- package/types/acdc/query-resend.d.ts +24 -0
- package/types/acdc/query-resend.d.ts.map +1 -0
- package/types/acdc/regengine.d.ts +19 -1
- package/types/acdc/regengine.d.ts.map +1 -1
- package/types/acdc/regeventing.d.ts.map +1 -1
- package/types/acdc/registering.d.ts +6 -0
- package/types/acdc/registering.d.ts.map +1 -1
- package/types/acdc/salting.d.ts.map +1 -1
- package/types/acdc/unblinding.d.ts +13 -1
- package/types/acdc/unblinding.d.ts.map +1 -1
- package/types/app/bootstrap/authenticating.d.ts +12 -2
- package/types/app/bootstrap/authenticating.d.ts.map +1 -1
- package/types/app/bootstrap/oobiery.d.ts +144 -7
- package/types/app/bootstrap/oobiery.d.ts.map +1 -1
- package/types/app/credentials/credential-importing.d.ts +8 -0
- package/types/app/credentials/credential-importing.d.ts.map +1 -1
- package/types/app/credentials/root-replicator.d.ts +19 -8
- package/types/app/credentials/root-replicator.d.ts.map +1 -1
- package/types/app/credentials/schema-resolving.d.ts.map +1 -1
- package/types/app/credentials/tel-publishing.d.ts +4 -2
- package/types/app/credentials/tel-publishing.d.ts.map +1 -1
- package/types/app/delivery/forwarding.d.ts +90 -5
- package/types/app/delivery/forwarding.d.ts.map +1 -1
- package/types/app/delivery/relaying.d.ts +144 -0
- package/types/app/delivery/relaying.d.ts.map +1 -0
- package/types/app/delivery/respondant.d.ts +9 -2
- package/types/app/delivery/respondant.d.ts.map +1 -1
- package/types/app/exchanges/exchanging.d.ts +36 -0
- package/types/app/exchanges/exchanging.d.ts.map +1 -1
- package/types/app/identity/delegating.d.ts +6 -0
- package/types/app/identity/delegating.d.ts.map +1 -1
- package/types/app/identity/habbing.d.ts +106 -19
- package/types/app/identity/habbing.d.ts.map +1 -1
- package/types/app/identity/keeping.d.ts +54 -19
- package/types/app/identity/keeping.d.ts.map +1 -1
- package/types/app/identity/retire-identifier.d.ts +24 -0
- package/types/app/identity/retire-identifier.d.ts.map +1 -0
- package/types/app/ipex/disclosure-replaying.d.ts +61 -0
- package/types/app/ipex/disclosure-replaying.d.ts.map +1 -0
- package/types/app/ipex/group-ipex.d.ts +60 -14
- package/types/app/ipex/group-ipex.d.ts.map +1 -1
- package/types/app/ipex/group-issuance.d.ts +54 -8
- package/types/app/ipex/group-issuance.d.ts.map +1 -1
- package/types/app/ipex/ipex-discloser.d.ts +39 -2
- package/types/app/ipex/ipex-discloser.d.ts.map +1 -1
- package/types/app/ipex/ipex-grouping.d.ts +23 -2
- package/types/app/ipex/ipex-grouping.d.ts.map +1 -1
- package/types/app/ipex/ipex-v2.d.ts +5 -2
- package/types/app/ipex/ipex-v2.d.ts.map +1 -1
- package/types/app/ipex/issuance.d.ts +46 -16
- package/types/app/ipex/issuance.d.ts.map +1 -1
- package/types/app/ipex/pending-presentations.d.ts +4 -2
- package/types/app/ipex/pending-presentations.d.ts.map +1 -1
- package/types/app/ipex/presenting.d.ts.map +1 -1
- package/types/app/ipex/receive-issuance.d.ts +2 -1
- package/types/app/ipex/receive-issuance.d.ts.map +1 -1
- package/types/app/ipex/unblinding-workflow.d.ts +13 -2
- package/types/app/ipex/unblinding-workflow.d.ts.map +1 -1
- package/types/app/mailbox/agent-inbox.d.ts +126 -0
- package/types/app/mailbox/agent-inbox.d.ts.map +1 -0
- package/types/app/mailbox/forward-handler.d.ts +114 -14
- package/types/app/mailbox/forward-handler.d.ts.map +1 -1
- package/types/app/mailbox/kel-publisher.d.ts +12 -22
- package/types/app/mailbox/kel-publisher.d.ts.map +1 -1
- package/types/app/mailbox/mailbox-poller.d.ts +40 -24
- package/types/app/mailbox/mailbox-poller.d.ts.map +1 -1
- package/types/app/mailbox/mailboxing.d.ts +19 -5
- package/types/app/mailbox/mailboxing.d.ts.map +1 -1
- package/types/app/multisig/counseling.d.ts +32 -11
- package/types/app/multisig/counseling.d.ts.map +1 -1
- package/types/app/multisig/group-members.d.ts +17 -1
- package/types/app/multisig/group-members.d.ts.map +1 -1
- package/types/app/multisig/grouping.d.ts +17 -1
- package/types/app/multisig/grouping.d.ts.map +1 -1
- package/types/app/multisig/multisig-workflows.d.ts +0 -8
- package/types/app/multisig/multisig-workflows.d.ts.map +1 -1
- package/types/app/multisig/proposing.d.ts +4 -19
- package/types/app/multisig/proposing.d.ts.map +1 -1
- package/types/app/multisig/rejoin-group.d.ts +119 -0
- package/types/app/multisig/rejoin-group.d.ts.map +1 -0
- package/types/app/multisig/replace-member.d.ts +15 -1
- package/types/app/multisig/replace-member.d.ts.map +1 -1
- package/types/app/protocol/parsering.d.ts +21 -2
- package/types/app/protocol/parsering.d.ts.map +1 -1
- package/types/app/protocol/protocol-processing.d.ts +25 -5
- package/types/app/protocol/protocol-processing.d.ts.map +1 -1
- package/types/app/queries/query-replies.d.ts +4 -8
- package/types/app/queries/query-replies.d.ts.map +1 -1
- package/types/app/queries/relay-refresh.d.ts +26 -0
- package/types/app/queries/relay-refresh.d.ts.map +1 -0
- package/types/app/queries/witness-inquisitor.d.ts +56 -5
- package/types/app/queries/witness-inquisitor.d.ts.map +1 -1
- package/types/app/runtime/agent-runtime.d.ts +34 -3
- package/types/app/runtime/agent-runtime.d.ts.map +1 -1
- package/types/app/runtime/cue-runtime.d.ts +15 -3
- package/types/app/runtime/cue-runtime.d.ts.map +1 -1
- package/types/app/runtime/ipex-auxiliaries.d.ts.map +1 -1
- package/types/app/runtime/operation-owners.d.ts +16 -26
- package/types/app/runtime/operation-owners.d.ts.map +1 -1
- package/types/app/runtime/workflow-contract.d.ts +21 -6
- package/types/app/runtime/workflow-contract.d.ts.map +1 -1
- package/types/app/transport/http-client.d.ts +9 -0
- package/types/app/transport/http-client.d.ts.map +1 -1
- package/types/app/transport/message-source.d.ts +6 -3
- package/types/app/transport/message-source.d.ts.map +1 -1
- package/types/app/transport/messengering.d.ts +10 -1
- package/types/app/transport/messengering.d.ts.map +1 -1
- package/types/app/witness/ingress.d.ts +1 -0
- package/types/app/witness/ingress.d.ts.map +1 -1
- package/types/app/witness/receipt-client.d.ts +19 -1
- package/types/app/witness/receipt-client.d.ts.map +1 -1
- package/types/app/witness/receipting.d.ts +24 -1
- package/types/app/witness/receipting.d.ts.map +1 -1
- package/types/cli/messaging/multisig.d.ts.map +1 -1
- package/types/core/cues.d.ts +53 -1
- package/types/core/cues.d.ts.map +1 -1
- package/types/core/errors.d.ts +32 -0
- package/types/core/errors.d.ts.map +1 -1
- package/types/core/kel/delegation-ancestry.d.ts.map +1 -1
- package/types/core/kel/eventing.d.ts.map +1 -1
- package/types/core/kel/kever-decisions.d.ts +3 -1
- package/types/core/kel/kever-decisions.d.ts.map +1 -1
- package/types/core/kel/kever.d.ts +7 -1
- package/types/core/kel/kever.d.ts.map +1 -1
- package/types/core/mailbox/kram-policy.d.ts +12 -0
- package/types/core/mailbox/kram-policy.d.ts.map +1 -1
- package/types/core/mailbox/kram-pruning.d.ts +5 -1
- package/types/core/mailbox/kram-pruning.d.ts.map +1 -1
- package/types/core/mailbox/kram-time.d.ts +26 -2
- package/types/core/mailbox/kram-time.d.ts.map +1 -1
- package/types/core/mailbox/kraming.d.ts +14 -1
- package/types/core/mailbox/kraming.d.ts.map +1 -1
- package/types/core/missing-anchor-query.d.ts +34 -0
- package/types/core/missing-anchor-query.d.ts.map +1 -0
- package/types/core/routing.d.ts +3 -7
- package/types/core/routing.d.ts.map +1 -1
- package/types/core/scheming.d.ts.map +1 -1
- package/types/core/wire-fields.d.ts +56 -0
- package/types/core/wire-fields.d.ts.map +1 -0
- package/types/db/basing.d.ts.map +1 -1
- package/types/db/core/indexeddb-persistence.d.ts +3 -2
- package/types/db/core/indexeddb-persistence.d.ts.map +1 -1
- package/types/db/core/webdber.d.ts +32 -6
- package/types/db/core/webdber.d.ts.map +1 -1
- package/types/db/exchange/exchange-records.d.ts +4 -0
- package/types/db/exchange/exchange-records.d.ts.map +1 -1
- package/types/db/mailbox/mailboxing.d.ts +10 -1
- package/types/db/mailbox/mailboxing.d.ts.map +1 -1
- package/types/db/subing.d.ts +2 -0
- package/types/db/subing.d.ts.map +1 -1
- package/types/runtime/index.d.ts +20 -14
- package/types/runtime/index.d.ts.map +1 -1
- package/types/time/mod.d.ts +2 -0
- package/types/time/mod.d.ts.map +1 -1
- package/types/vdr/credential-tsn-record.d.ts.map +1 -1
- package/types/vdr/eventing.d.ts +32 -1
- package/types/vdr/eventing.d.ts.map +1 -1
- package/types/vdr/registry-tsn-record.d.ts.map +1 -1
- package/types/version.d.ts +2 -2
- package/esm/keri/npm/src/app/runtime/signer-conditions.js +0 -51
- package/types/app/runtime/signer-conditions.d.ts +0 -32
- package/types/app/runtime/signer-conditions.d.ts.map +0 -1
|
@@ -16,6 +16,9 @@
|
|
|
16
16
|
* | holder custodian or member | {@link approveGroupIssuanceExchange} | consent to DH (contributions follow as they arrive) |
|
|
17
17
|
* | a member that only follows | {@link followGroupIssuanceProof} | nothing; verifies the custodian's final proof |
|
|
18
18
|
*
|
|
19
|
+
* The follow is not a consent and needs no call: the owner begins it itself (below). The explicit call remains for a
|
|
20
|
+
* proof supplied as bytes and is idempotent with the automatic follow.
|
|
21
|
+
*
|
|
19
22
|
* The registry update is a separate consent: the custodian proposes the BUP through `proposeRegistryEvent`
|
|
20
23
|
* (its own contribution is its approval) and every other issuer member approves that proposal with
|
|
21
24
|
* `approveGroupProposal`. Admit is a separate consent as well.
|
|
@@ -25,20 +28,30 @@
|
|
|
25
28
|
* Grant and the proof broadcast to the other members. Root copies are not sent here: the DH completion cue
|
|
26
29
|
* feeds the runtime's `RootReplicator`, which sends exactly one copy per member.
|
|
27
30
|
*
|
|
31
|
+
* Automatic follow: on its slow sweep of the pending exchange escrow the owner begins the proof-only follow, in either
|
|
32
|
+
* role, of each pending group Grant inside its deadline that this member has no live, ended or complete operation for
|
|
33
|
+
* and whose custodian's final proof is stored ({@link storedGroupProof}). The proof exists only after the key exchange
|
|
34
|
+
* finished, so the follow never takes the place of a member whose signature is still wanted; until the proof or a root
|
|
35
|
+
* copy is stored the member reads `awaitingExchangeConsent` and consents as usual. A member holding a root copy or the
|
|
36
|
+
* proof reads `awaitingAnchor` ({@link awaitsGroupProof}) until the follow accepts the Grant.
|
|
37
|
+
*
|
|
28
38
|
* Reload: nothing about a group operation is durable but the protocol stores. A member that reloads reads
|
|
29
39
|
* `awaitingExchangeConsent` for a pending Grant it holds no root for and consents again with the same call;
|
|
30
40
|
* its signatures are deterministic, so consenting twice produces the same bytes. A custodian that reloaded
|
|
31
|
-
* after retaining its root resumes when the stores allow it ({@link deriveGroupCustody}).
|
|
41
|
+
* after retaining its root resumes when the stores allow it ({@link deriveGroupCustody}). A follow signs nothing, so
|
|
42
|
+
* the first sweep after a reload begins it again from the stored proof.
|
|
32
43
|
*/
|
|
33
44
|
import { bytesEqual, collectPayload, concatBytes, Counter, framePayload, parseBytes, SerderACDC, SerderKERI, Vrsn_2_0, } from "cesr-ts";
|
|
34
45
|
import { call } from "effection";
|
|
35
46
|
import { RegistryAuthor } from "../../acdc/authoring.js";
|
|
36
47
|
import { RegistryCustody } from "../../acdc/custody.js";
|
|
37
48
|
import { Dhke, grantTransactionId } from "../../acdc/dhking.js";
|
|
49
|
+
import { ThresholdQueryResend } from "../../acdc/query-resend.js";
|
|
38
50
|
import { vet } from "../../acdc/regeventing.js";
|
|
39
51
|
import { RegistryRoot } from "../../acdc/salting.js";
|
|
40
52
|
import { ValidationError } from "../../core/errors.js";
|
|
41
53
|
import { Kever } from "../../core/kel/kever.js";
|
|
54
|
+
import { multiKeyQueryWindow } from "../../core/mailbox/kram-policy.js";
|
|
42
55
|
import { messagize } from "../../core/protocol-serialization.js";
|
|
43
56
|
import { loadNests } from "../../db/exchange/exchange-nests.js";
|
|
44
57
|
import { timestampMicros } from "../../time/mod.js";
|
|
@@ -197,29 +210,76 @@ export function deriveGroupCustody(deps, hab, grant) {
|
|
|
197
210
|
if (!isLocalGroupHab(hby, hab) || !hab.kever)
|
|
198
211
|
return null;
|
|
199
212
|
const member = localGroupMember(hby, hab.pre);
|
|
200
|
-
const registries =
|
|
201
|
-
for (const nest of deps.exc.retainedInput(grant)?.nests ?? []) {
|
|
202
|
-
const rd = nest.body.ked?.rd;
|
|
203
|
-
if (typeof rd === "string")
|
|
204
|
-
registries.add(rd);
|
|
205
|
-
}
|
|
213
|
+
const registries = disclosedRegistries(deps.exc, grant);
|
|
206
214
|
if (!registries.size || [...registries].some((registry) => !hab.mgr.hasRegistryRoot(hab.pre, registry))) {
|
|
207
215
|
return null;
|
|
208
216
|
}
|
|
209
217
|
if (receivedRootCopy(hby, hab.pre, member.pre, registries))
|
|
210
218
|
return null;
|
|
211
|
-
const
|
|
219
|
+
const coordinator = electingCoordinator(hby, hab.pre, member.pre);
|
|
220
|
+
return coordinator ? { coordinator, custodian: member.pre, lead: true } : null;
|
|
221
|
+
}
|
|
222
|
+
/** The registries a retained Grant discloses credentials of. */
|
|
223
|
+
function disclosedRegistries(exc, grant) {
|
|
224
|
+
const registries = new Set();
|
|
225
|
+
for (const nest of exc.retainedInput(grant)?.nests ?? []) {
|
|
226
|
+
const rd = nest.body.ked?.rd;
|
|
227
|
+
if (typeof rd === "string")
|
|
228
|
+
registries.add(rd);
|
|
229
|
+
}
|
|
230
|
+
return registries;
|
|
231
|
+
}
|
|
232
|
+
/** The latest of the group's recent accepted events whose threshold evidence elects `member`, or null. */
|
|
233
|
+
function electingCoordinator(hby, group, member) {
|
|
234
|
+
const events = [...hby.db.getKelItemIter(group)].slice(-GROUP_COORDINATOR_SEARCH);
|
|
212
235
|
for (const [, said] of events.reverse()) {
|
|
213
|
-
const event = hby.db.getEvtSerder(
|
|
236
|
+
const event = hby.db.getEvtSerder(group, said);
|
|
214
237
|
if (!event || event.sn === null)
|
|
215
238
|
continue;
|
|
216
239
|
const coordinator = { sn: event.sn, said: event.said };
|
|
217
|
-
if (groupElectedMember(hby,
|
|
218
|
-
return
|
|
219
|
-
}
|
|
240
|
+
if (groupElectedMember(hby, group, coordinator) === member)
|
|
241
|
+
return coordinator;
|
|
220
242
|
}
|
|
221
243
|
return null;
|
|
222
244
|
}
|
|
245
|
+
/**
|
|
246
|
+
* The election of a final proof the local member of `hab` can follow for `grant` without taking part: the
|
|
247
|
+
* `/multisig/ipex-proof` wrapper of this exact Grant that another current signing member published, with a recent
|
|
248
|
+
* accepted group event electing that member as custodian. The custodian publishes the proof only after the key
|
|
249
|
+
* exchange finished and the registry update was accepted, so once it is stored no member's signature is still wanted
|
|
250
|
+
* for this issuance. The sender names the custodian exactly, which a root copy cannot (any member holding a root
|
|
251
|
+
* sends copies). Null when no such proof is stored. A pure read of the stored exchanges.
|
|
252
|
+
*/
|
|
253
|
+
export function storedGroupProof(deps, hab, grant) {
|
|
254
|
+
const { hby } = deps;
|
|
255
|
+
if (!isLocalGroupHab(hby, hab) || !hab.kever)
|
|
256
|
+
return null;
|
|
257
|
+
const member = localGroupMember(hby, hab.pre).pre, signing = groupSigningMembers(hby, hab.pre);
|
|
258
|
+
for (const [, parent] of hby.db.exns.getTopItemIter()) {
|
|
259
|
+
const attrs = parent.sad?.a;
|
|
260
|
+
if (parent.route !== MULTISIG_IPEX_PROOF_ROUTE || attrs?.gid !== hab.pre || attrs?.grant !== grant
|
|
261
|
+
|| !parent.pre || parent.pre === member || !signing.includes(parent.pre))
|
|
262
|
+
continue;
|
|
263
|
+
const coordinator = electingCoordinator(hby, hab.pre, parent.pre);
|
|
264
|
+
if (coordinator)
|
|
265
|
+
return { coordinator, custodian: parent.pre, lead: false };
|
|
266
|
+
}
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Whether the group completed `grant` without the local member of `hab`, so that its runtime follows the final proof
|
|
271
|
+
* instead of asking it to consent: a `/registry/root` copy of a disclosed registry was sent to it (copies are
|
|
272
|
+
* authorized only once the registry update is accepted, after the key exchange), or the custodian's final proof is
|
|
273
|
+
* stored ({@link storedGroupProof}). A pure read of the stores.
|
|
274
|
+
*/
|
|
275
|
+
export function awaitsGroupProof(deps, hab, grant) {
|
|
276
|
+
const { hby } = deps;
|
|
277
|
+
if (!isLocalGroupHab(hby, hab) || !hab.kever)
|
|
278
|
+
return false;
|
|
279
|
+
const member = localGroupMember(hby, hab.pre).pre, registries = disclosedRegistries(deps.exc, grant);
|
|
280
|
+
return (registries.size > 0 && receivedRootCopy(hby, hab.pre, member, registries))
|
|
281
|
+
|| !!storedGroupProof(deps, hab, grant);
|
|
282
|
+
}
|
|
223
283
|
/** Whether `member` was sent a `/registry/root` copy of the group's root for any of `registries`. */
|
|
224
284
|
function receivedRootCopy(hby, group, member, registries) {
|
|
225
285
|
for (const [, copy] of hby.db.exns.getTopItemIter()) {
|
|
@@ -421,23 +481,51 @@ export function approveGroupIssuanceExchange(ops, said, coordinator) {
|
|
|
421
481
|
return ops.beginGroup(new GroupIssuance(party, "holder", grant, nodes));
|
|
422
482
|
}
|
|
423
483
|
/**
|
|
424
|
-
* Follow a group issuance
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
484
|
+
* Follow a group issuance only to verify and accept the custodian's final proof, signing nothing. It serves any
|
|
485
|
+
* member that does not contribute to the key exchange: an issuer member that left after contributing and returns
|
|
486
|
+
* for the proof, or a holder member the group completed without, such as one whose signing weight is 0. The group
|
|
487
|
+
* is the Grant's sender when that is a local group, otherwise its recipient. The proof may already be stored (it
|
|
488
|
+
* arrives by mailbox), or be supplied as bytes.
|
|
489
|
+
*
|
|
490
|
+
* The owner begins the same follow on its own once the custodian's proof is stored ({@link storedGroupProof}), so a
|
|
491
|
+
* host needs this call only to supply the proof as bytes or to pin the coordinator. It is idempotent with that
|
|
492
|
+
* follow; see {@link beginGroupProofFollow}.
|
|
428
493
|
*
|
|
429
|
-
* @throws ValidationError for a Grant
|
|
494
|
+
* @throws ValidationError for a Grant that is not pending for a local group, or one begun under a consent.
|
|
430
495
|
*/
|
|
431
496
|
export function* followGroupIssuanceProof(ops, said, options = {}) {
|
|
432
|
-
const { deps } = ops;
|
|
433
|
-
|
|
497
|
+
const { deps } = ops, sent = deps.exc.retainedInput(said)?.serder.pre;
|
|
498
|
+
const sender = sent ? deps.hby.habs.get(sent) : undefined;
|
|
499
|
+
const role = sender && isLocalGroupHab(deps.hby, sender) ? "issuer" : "holder";
|
|
500
|
+
const begun = beginGroupProofFollow(ops, said, role, options.coordinator);
|
|
501
|
+
// Stored for the follow's next scan, whether it began now or earlier.
|
|
502
|
+
if (options.proof)
|
|
503
|
+
yield* deps.reactor.processBytes(options.proof);
|
|
504
|
+
return begun;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Begin `role`'s proof-only follow of the pending group Grant `said` under `coordinator` (default the group's latest
|
|
508
|
+
* establishment event). Signs nothing. Idempotent: when that party's follow is already live, or its side of the Grant
|
|
509
|
+
* is already complete, returns the Grant and its transaction and begins nothing. The owner's sweep and
|
|
510
|
+
* {@link followGroupIssuanceProof} both begin follows here.
|
|
511
|
+
*
|
|
512
|
+
* @throws ValidationError for a Grant that is not pending for a local group, one whose live operation is a consent,
|
|
513
|
+
* or a coordinator {@link GroupParty.resolve} refuses.
|
|
514
|
+
*/
|
|
515
|
+
export function beginGroupProofFollow(ops, said, role, coordinator) {
|
|
516
|
+
const { deps } = ops, live = ops.liveOf(said, role);
|
|
517
|
+
if (live?.group?.mode === "proofOnly")
|
|
518
|
+
return { grant: said, xid: live.xid };
|
|
519
|
+
if (live)
|
|
434
520
|
throw new ValidationError("Issuance was already begun for this Grant");
|
|
521
|
+
if (partyComplete(deps, said, role)) {
|
|
522
|
+
const serder = deps.exc.retainedInput(said)?.serder;
|
|
523
|
+
return { grant: said, xid: serder ? grantTransactionId(serder) : said };
|
|
435
524
|
}
|
|
436
525
|
const { grant, nodes } = pendingGrant(deps, said);
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
return ops.beginGroup(new GroupIssuance(party, "issuer", grant, nodes, { mode: "proofOnly" }));
|
|
526
|
+
const group = localGroup(deps, role === "issuer" ? grant.pre : grant.sad.ri);
|
|
527
|
+
const party = GroupParty.resolve(deps, group, coordinator);
|
|
528
|
+
return ops.beginGroup(new GroupIssuance(party, role, grant, nodes, { mode: "proofOnly" }));
|
|
441
529
|
}
|
|
442
530
|
/**
|
|
443
531
|
* The custodian's exact public proof of a completed group issuance: the proof support and the
|
|
@@ -527,17 +615,12 @@ export class GroupIssuance {
|
|
|
527
615
|
writable: true,
|
|
528
616
|
value: void 0
|
|
529
617
|
});
|
|
530
|
-
|
|
618
|
+
/** The frozen threshold query and its resend schedule; a group cannot re-date it (`Dhke.requery` refuses groups). */
|
|
619
|
+
Object.defineProperty(this, "resend", {
|
|
531
620
|
enumerable: true,
|
|
532
621
|
configurable: true,
|
|
533
622
|
writable: true,
|
|
534
|
-
value:
|
|
535
|
-
});
|
|
536
|
-
Object.defineProperty(this, "nextQuery", {
|
|
537
|
-
enumerable: true,
|
|
538
|
-
configurable: true,
|
|
539
|
-
writable: true,
|
|
540
|
-
value: 0
|
|
623
|
+
value: new ThresholdQueryResend()
|
|
541
624
|
});
|
|
542
625
|
Object.defineProperty(this, "nextProofScan", {
|
|
543
626
|
enumerable: true,
|
|
@@ -565,6 +648,13 @@ export class GroupIssuance {
|
|
|
565
648
|
writable: true,
|
|
566
649
|
value: void 0
|
|
567
650
|
});
|
|
651
|
+
/** What the Grant send's proof support carried; absent when this operation did not send it (rebuilt after a restart). */
|
|
652
|
+
Object.defineProperty(this, "supportMark", {
|
|
653
|
+
enumerable: true,
|
|
654
|
+
configurable: true,
|
|
655
|
+
writable: true,
|
|
656
|
+
value: void 0
|
|
657
|
+
});
|
|
568
658
|
Object.defineProperty(this, "mode", {
|
|
569
659
|
enumerable: true,
|
|
570
660
|
configurable: true,
|
|
@@ -800,7 +890,9 @@ export function* stepGroup(ops, op) {
|
|
|
800
890
|
const input = deps.exc.retainedInput(grant, { pending: true });
|
|
801
891
|
const frame = parseBytes(messagize(input.serder, { tsgs: input.tsgs, cigars: input.cigars, gvrsn: Vrsn_2_0 }))[0];
|
|
802
892
|
frame.nests = [...input.nests ?? []];
|
|
803
|
-
const
|
|
893
|
+
const support = ops.discloser.proofSupport(g.nodeBytes, hab);
|
|
894
|
+
g.supportMark = ops.discloser.proofSupportMark(g.nodeBytes, hab);
|
|
895
|
+
const full = concatBytes(support, yield* wire(frame));
|
|
804
896
|
for (const peer of party.others())
|
|
805
897
|
yield* send(peer, full);
|
|
806
898
|
if (issuer) {
|
|
@@ -854,13 +946,17 @@ export function* stepGroup(ops, op) {
|
|
|
854
946
|
yield* broadcast(yield* party.wrap(child, MULTISIG_RPY_ROUTE));
|
|
855
947
|
}
|
|
856
948
|
else if (message.kin === "query")
|
|
857
|
-
g.
|
|
949
|
+
g.resend.set(message.msg);
|
|
858
950
|
else
|
|
859
951
|
yield* send(message.dest, message.msg, "credential");
|
|
860
952
|
}
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
953
|
+
// The unchanged query is resent with backoff while the holder can still accept it; the operation keeps waiting
|
|
954
|
+
// for the holder past that, to its own deadline.
|
|
955
|
+
const now = deps.clock.now(), base = deps.queryResendMs ?? ISSUANCE_QUERY_RESEND_MS;
|
|
956
|
+
if (g.resend.ready(now) && g.resend.fresh(now, multiKeyQueryWindow(deps.hby.db, Dhke.Route))
|
|
957
|
+
&& !dh.complete(xid)) {
|
|
958
|
+
yield* send(g.grant.sad.ri, g.resend.query, "credential");
|
|
959
|
+
g.resend.sent(now, base);
|
|
864
960
|
}
|
|
865
961
|
if (!lead || !dh.complete(xid))
|
|
866
962
|
return false;
|
|
@@ -894,7 +990,11 @@ export function* stepGroup(ops, op) {
|
|
|
894
990
|
if (!deps.exc.complete(grant))
|
|
895
991
|
throw new ValidationError("Issued group Grant remains unaccepted");
|
|
896
992
|
const completed = parseBytes(yield* storedBytes(deps.exc, grant))[0];
|
|
897
|
-
|
|
993
|
+
// The Grant send already carried the support; proving added the anchoring event and the registry update. An
|
|
994
|
+
// operation without a mark did not send it, so it sends in full.
|
|
995
|
+
const support = g.supportMark
|
|
996
|
+
? ops.discloser.proofSupportSince(g.nodeBytes, hab, g.supportMark)
|
|
997
|
+
: ops.discloser.proofSupport(g.nodeBytes, hab);
|
|
898
998
|
// KERIpy form: the elected member shares the proof with the other members by mailbox.
|
|
899
999
|
const wrapped = yield* party.wrap(completed, MULTISIG_IPEX_PROOF_ROUTE);
|
|
900
1000
|
// Kept locally, so the proof the members received is the one {@link groupIssuanceProof} exports.
|
|
@@ -88,6 +88,32 @@ export function* storedExchangeWire(retained, gvrsn = Vrsn_2_0) {
|
|
|
88
88
|
frame.nests = nests;
|
|
89
89
|
return yield* call(() => collectPayload(framePayload(frame)));
|
|
90
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Extend a frozen Grant's exact origin proof with its presented-state opening and the KEL seal of the registry update
|
|
93
|
+
* that commits to it; the Grant body and its SAID are unchanged. Ordinary intake still authenticates the supplied
|
|
94
|
+
* presentation evidence. Reads no store and signs nothing.
|
|
95
|
+
*
|
|
96
|
+
* @throws ValidationError when the opening does not target this exact Grant's disclosed Issuee in the `presented` state.
|
|
97
|
+
*/
|
|
98
|
+
export function* attachPresentationProof(prepared, opening, source) {
|
|
99
|
+
const frames = parseBytes(prepared.raw), node = frames[0].body;
|
|
100
|
+
if (!node.issuee || opening.td.nonce !== prepared.exn.said || opening.ts.label !== "presented") {
|
|
101
|
+
throw new ValidationError("Presentation proof must target this exact Grant and disclosed Issuee.");
|
|
102
|
+
}
|
|
103
|
+
const seal = Counter.enclose({
|
|
104
|
+
code: "SealSourceTriples",
|
|
105
|
+
qb64: concatBytes(new Prefixer({ qb64: node.issuee }).qb64b, source.s.qb64b, source.d.qb64b),
|
|
106
|
+
version: Vrsn_2_0,
|
|
107
|
+
});
|
|
108
|
+
const first = concatBytes(node.raw, Counter.enclose({
|
|
109
|
+
qb64: concatBytes(...frames[0].attachments.map(group => group.raw), publicOpeningAttachment(opening), seal),
|
|
110
|
+
version: Vrsn_2_0,
|
|
111
|
+
}));
|
|
112
|
+
const rest = yield* call(() => Promise.all(frames.slice(1).map(frame => collectPayload(nestedPayload(frame, Vrsn_2_0)))));
|
|
113
|
+
const raw = concatBytes(Counter.makeGVC(Vrsn_2_0), first, ...rest);
|
|
114
|
+
const payloads = yield* call(() => Promise.all(parseBytes(raw).map(frame => collectPayload(nestedPayload(frame, Vrsn_2_0)))));
|
|
115
|
+
return { exn: prepared.exn, raw, nests: concatBytes(...payloads) };
|
|
116
|
+
}
|
|
91
117
|
/** Borrow concrete owners only. No constructor opens a store, starts a worker or loads registry secrets. */
|
|
92
118
|
export class IpexDiscloser {
|
|
93
119
|
constructor(hby, registry, exc, receiptor, custody, views) {
|
|
@@ -201,7 +227,65 @@ export class IpexDiscloser {
|
|
|
201
227
|
}
|
|
202
228
|
/** Carry selected public evidence and root-first delegation ancestry; receiver intake still verifies every approval. */
|
|
203
229
|
proofSupport(raw, hab) {
|
|
204
|
-
|
|
230
|
+
return this.proofSupportFor(raw, [hab.pre]);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* `proofSupport` for explicitly named parties instead of one habitat: the KELs of `parties` and of every identifier
|
|
234
|
+
* the ACDC frames in `raw` name, each after its delegation ancestry, then the accepted TEL of every registry they
|
|
235
|
+
* name. Reads accepted state only and signs nothing, so it needs no keys.
|
|
236
|
+
*/
|
|
237
|
+
proofSupportFor(raw, parties) {
|
|
238
|
+
const { ordered, registries } = this.supportSelection(raw, parties);
|
|
239
|
+
const messages = [Counter.makeGVC(Vrsn_2_0)];
|
|
240
|
+
// Keep first-seen authorizers and exact stored attachments, including superseded history.
|
|
241
|
+
// clonePreIter skips broken rows; finite disclosure must instead fail if an accepted row cannot be replayed.
|
|
242
|
+
for (const aid of ordered)
|
|
243
|
+
messages.push(...this.supportKel(aid, 0));
|
|
244
|
+
for (const regid of registries)
|
|
245
|
+
messages.push(this.registry.cloneTel(regid));
|
|
246
|
+
return concatBytes(...messages);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Record how far `proofSupport(raw, hab)` reaches right now: each KEL's last first-seen ordinal and each
|
|
250
|
+
* registry's last TEL ordinal. Call it in the same synchronous turn as the send it describes, so the mark is
|
|
251
|
+
* exactly what that send carried; {@link proofSupportSince} then sends what accepted state has gained.
|
|
252
|
+
*/
|
|
253
|
+
proofSupportMark(raw, hab) {
|
|
254
|
+
const { ordered, registries } = this.supportSelection(raw, [hab.pre]);
|
|
255
|
+
const kels = new Map(), tels = new Map();
|
|
256
|
+
for (const aid of ordered) {
|
|
257
|
+
let last = -1;
|
|
258
|
+
for (const [, fn] of this.hby.db.fels.getAllItemIter(aid))
|
|
259
|
+
last = fn;
|
|
260
|
+
kels.set(aid, last);
|
|
261
|
+
}
|
|
262
|
+
for (const regid of registries) {
|
|
263
|
+
let last = -1;
|
|
264
|
+
for (const [, sn] of this.registry.baser.tels.getAllItemIter(regid))
|
|
265
|
+
last = sn;
|
|
266
|
+
tels.set(regid, last);
|
|
267
|
+
}
|
|
268
|
+
return { kels, tels };
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* The part of `proofSupport(raw, hab)` that accepted state gained after `mark` was taken, in the same order and
|
|
272
|
+
* with the same stored attachments: later first-seen KEL events of each identifier (the anchoring interaction
|
|
273
|
+
* event of a registry update, say) and later TEL events of each registry. An identifier or registry the mark does
|
|
274
|
+
* not name (support that grew a new party) is carried whole. A recipient that holds what `mark` describes holds
|
|
275
|
+
* the same state after this as after a second full `proofSupport`.
|
|
276
|
+
*/
|
|
277
|
+
proofSupportSince(raw, hab, mark) {
|
|
278
|
+
const { ordered, registries } = this.supportSelection(raw, [hab.pre]);
|
|
279
|
+
const messages = [Counter.makeGVC(Vrsn_2_0)];
|
|
280
|
+
for (const aid of ordered)
|
|
281
|
+
messages.push(...this.supportKel(aid, (mark.kels.get(aid) ?? -1) + 1));
|
|
282
|
+
for (const regid of registries)
|
|
283
|
+
messages.push(this.registry.cloneTel(regid, (mark.tels.get(regid) ?? -1) + 1));
|
|
284
|
+
return concatBytes(...messages);
|
|
285
|
+
}
|
|
286
|
+
/** The identifiers (each after its delegation ancestry) and registries that support for `raw` and `parties` names. */
|
|
287
|
+
supportSelection(raw, parties) {
|
|
288
|
+
const aids = new Set(parties), registries = new Set();
|
|
205
289
|
for (const frame of parseBytes(raw)) {
|
|
206
290
|
if (!(frame.body instanceof SerderACDC))
|
|
207
291
|
continue;
|
|
@@ -222,7 +306,6 @@ export class IpexDiscloser {
|
|
|
222
306
|
aids.add(seal.i.qb64);
|
|
223
307
|
}
|
|
224
308
|
}
|
|
225
|
-
const messages = [Counter.makeGVC(Vrsn_2_0)];
|
|
226
309
|
const db = this.hby.db, ordered = new Set();
|
|
227
310
|
// An ancestry claim selects supporting KELs; it does not establish delegation authority.
|
|
228
311
|
for (const aid of aids) {
|
|
@@ -241,15 +324,15 @@ export class IpexDiscloser {
|
|
|
241
324
|
for (const ancestor of ancestors.reverse())
|
|
242
325
|
ordered.add(ancestor);
|
|
243
326
|
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
327
|
+
return { ordered, registries };
|
|
328
|
+
}
|
|
329
|
+
/** Stored first-seen events of `aid` from ordinal `from`, each with its exact stored attachments. */
|
|
330
|
+
supportKel(aid, from) {
|
|
331
|
+
const messages = [];
|
|
332
|
+
for (const [, fn, said] of this.hby.db.fels.getAllItemIter(aid, from)) {
|
|
333
|
+
messages.push(this.hby.db.cloneEvtMsg(aid, fn, said, Vrsn_2_0));
|
|
249
334
|
}
|
|
250
|
-
|
|
251
|
-
messages.push(this.registry.cloneTel(regid));
|
|
252
|
-
return concatBytes(...messages);
|
|
335
|
+
return messages;
|
|
253
336
|
}
|
|
254
337
|
/** Public replay includes actual stored witness receipts; it grants no new consent. */
|
|
255
338
|
kel(hab) {
|
|
@@ -399,26 +482,6 @@ export class IpexDiscloser {
|
|
|
399
482
|
const prior = yield* this.prior(priorSaid);
|
|
400
483
|
return ipexExnBody(sender, `/ipex/${verb}`, [], { message, prior, recipient });
|
|
401
484
|
}
|
|
402
|
-
/** Extend the frozen Grant's exact origin proof; ordinary intake still authenticates the supplied presentation evidence. */
|
|
403
|
-
*attachPresentationProof(prepared, opening, source) {
|
|
404
|
-
const frames = parseBytes(prepared.raw), node = frames[0].body;
|
|
405
|
-
if (!node.issuee || opening.td.nonce !== prepared.exn.said || opening.ts.label !== "presented") {
|
|
406
|
-
throw new ValidationError("Presentation proof must target this exact Grant and disclosed Issuee.");
|
|
407
|
-
}
|
|
408
|
-
const seal = Counter.enclose({
|
|
409
|
-
code: "SealSourceTriples",
|
|
410
|
-
qb64: concatBytes(new Prefixer({ qb64: node.issuee }).qb64b, source.s.qb64b, source.d.qb64b),
|
|
411
|
-
version: Vrsn_2_0,
|
|
412
|
-
});
|
|
413
|
-
const first = concatBytes(node.raw, Counter.enclose({
|
|
414
|
-
qb64: concatBytes(...frames[0].attachments.map(group => group.raw), publicOpeningAttachment(opening), seal),
|
|
415
|
-
version: Vrsn_2_0,
|
|
416
|
-
}));
|
|
417
|
-
const rest = yield* call(() => Promise.all(frames.slice(1).map(frame => collectPayload(nestedPayload(frame, Vrsn_2_0)))));
|
|
418
|
-
const raw = concatBytes(Counter.makeGVC(Vrsn_2_0), first, ...rest);
|
|
419
|
-
const payloads = yield* call(() => Promise.all(parseBytes(raw).map(frame => collectPayload(nestedPayload(frame, Vrsn_2_0)))));
|
|
420
|
-
return { exn: prepared.exn, raw, nests: concatBytes(...payloads) };
|
|
421
|
-
}
|
|
422
485
|
/** Select immutable admitted/transferred nests; proof refresh changes attachments only when explicitly requested. */
|
|
423
486
|
*selected(selection, offer = false) {
|
|
424
487
|
if ([selection.said, selection.fromGrant, selection.transferred].filter(value => value !== undefined).length !== 1) {
|
|
@@ -540,7 +603,7 @@ export class IpexDiscloser {
|
|
|
540
603
|
if (reg.store.seqEvent(reg.regk, Number(BigInt(`0x${update.sad.n}`)))?.said !== update.said) {
|
|
541
604
|
throw new ValidationError("Presentation BUP remains pending authenticated acceptance.");
|
|
542
605
|
}
|
|
543
|
-
({ raw, nests } = yield*
|
|
606
|
+
({ raw, nests } = yield* attachPresentationProof({ exn, raw, nests }, proof, seal));
|
|
544
607
|
}
|
|
545
608
|
const source = exn.sad.a.ax?.[0]
|
|
546
609
|
? yield* anchorExchange(this.hby, this.receiptor, hab, exn, options.auths)
|
|
@@ -12,7 +12,7 @@ import { call } from "effection";
|
|
|
12
12
|
import { evaluateChain } from "../../acdc/chaining.js";
|
|
13
13
|
import { resolveAttachmentGvrsn } from "../../core/attachment-countering.js";
|
|
14
14
|
import { TransIdxSigGroup } from "../../core/dispatch.js";
|
|
15
|
-
import { ValidationError } from "../../core/errors.js";
|
|
15
|
+
import { KramRefusedError, ValidationError } from "../../core/errors.js";
|
|
16
16
|
import { verifyExchangeProofs } from "../../core/exchange/exchange-proofing.js";
|
|
17
17
|
import { exchange, serializeMessage } from "../../core/exchange/protocol-exchanging.js";
|
|
18
18
|
import { Kever } from "../../core/kel/kever.js";
|
|
@@ -74,7 +74,19 @@ export function requireAcdcV2GroupUse(hby, protocol, embedded) {
|
|
|
74
74
|
throw new ValidationError("ACDC v2 group graph is unavailable for completed use.");
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
|
-
/**
|
|
77
|
+
/** KRAM refusals after which the message can no longer be accepted; a `replay` is routine, not one of them. */
|
|
78
|
+
const KRAM_EXPIRY_REASONS = new Set(["timestamp", "timeliness", "unknownThread", "lifetime"]);
|
|
79
|
+
/** Whether `decision` is this receiver's KRAM refusing a message for its time, its thread or its lifetime. */
|
|
80
|
+
function isKramExpiry(decision) {
|
|
81
|
+
return !!decision && decision.kind === "drop" && "reason" in decision && KRAM_EXPIRY_REASONS.has(decision.reason);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Approve exactly one ACDC v2 IPEX stream before adding this member's signature; no new acceptance rows are manufactured.
|
|
85
|
+
*
|
|
86
|
+
* @returns This member's accepted wire, or null while its evidence or dependencies are missing.
|
|
87
|
+
* @throws KramRefusedError when this member's own KRAM refuses the contribution for its time, thread or exchange
|
|
88
|
+
* lifetime: the message can no longer be accepted here. A `replay` refusal is routine and is not thrown.
|
|
89
|
+
*/
|
|
78
90
|
export function* prepareAcdcV2GroupIpex(hby, protocol, group, raw) {
|
|
79
91
|
const frames = parseBytes(concatBytes(Counter.makeGVC(Vrsn_2_0), raw));
|
|
80
92
|
const frame = frames[0], embedded = frame?.body;
|
|
@@ -136,7 +148,11 @@ export function* prepareAcdcV2GroupIpex(hby, protocol, group, raw) {
|
|
|
136
148
|
};
|
|
137
149
|
const attachments = yield* call(() => collectPayload(attachmentPayload(approved, { gvrsn: Vrsn_2_0 })));
|
|
138
150
|
const wire = concatBytes(embedded.raw, attachments);
|
|
139
|
-
|
|
151
|
+
// Dispatch the frame itself, not its bytes, so this member's own KRAM decision is visible. A refusal here is
|
|
152
|
+
// not a remote outcome to skip: the contribution was never accepted and nothing else will complete the message.
|
|
153
|
+
const { decision } = yield* protocol.dispatch(parseBytes(wire)[0], { local: true }, new Reactor(protocol).kvy);
|
|
154
|
+
if (isKramExpiry(decision))
|
|
155
|
+
throw new KramRefusedError(embedded.said, decision);
|
|
140
156
|
yield* protocol.processEscrowsOnce();
|
|
141
157
|
return wire;
|
|
142
158
|
}
|
|
@@ -457,6 +473,40 @@ export function* approveStoredMultisigIpexWrappers(hby, protocol, embeddedSaid,
|
|
|
457
473
|
yield* approveIpexChild(hby, protocol, groupHab, child, wrapper, reger);
|
|
458
474
|
}
|
|
459
475
|
}
|
|
476
|
+
/**
|
|
477
|
+
* The copies this member received of each v2 group IPEX message it has not accepted: per message, the child of each
|
|
478
|
+
* stored, authenticated `/multisig/exn` wrapper that carries it, with its signatures and nests. A pure read; whether a
|
|
479
|
+
* message may be taken from its copies is {@link groupIpexFollowable}'s rule.
|
|
480
|
+
*/
|
|
481
|
+
export function unacceptedGroupIpexCopies(hby, exchanger) {
|
|
482
|
+
const copies = new Map();
|
|
483
|
+
for (const [, wrapper] of hby.db.exns.getTopItemIter()) {
|
|
484
|
+
if (wrapper.route !== MULTISIG_EXN_ROUTE || wrapper.pvrsn.major !== 2)
|
|
485
|
+
continue;
|
|
486
|
+
const said = storedMultisigIpexBody(hby.db, wrapper)?.said;
|
|
487
|
+
if (!said || exchanger.complete(said) || !exchanger.verifyAccepted(wrapper.said, MULTISIG_EXN_ROUTE))
|
|
488
|
+
continue;
|
|
489
|
+
copies.set(said, [...copies.get(said) ?? [], loadNests(hby.db, wrapper.said)[0]]);
|
|
490
|
+
}
|
|
491
|
+
return [...copies.values()];
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* Accept a group IPEX message from the copies this member received, adding no signature: dispatch each copy with its
|
|
495
|
+
* own proofs, then advance escrows so that copies whose signatures only together meet the threshold complete. The
|
|
496
|
+
* caller has checked {@link groupIpexFollowable}.
|
|
497
|
+
*
|
|
498
|
+
* @throws KramRefusedError when this member's own KRAM refuses a copy for its time, thread or exchange lifetime: the
|
|
499
|
+
* message can no longer be accepted here.
|
|
500
|
+
*/
|
|
501
|
+
export function* acceptGroupIpexCopies(protocol, copies) {
|
|
502
|
+
const kvy = new Reactor(protocol).kvy;
|
|
503
|
+
for (const copy of copies) {
|
|
504
|
+
const { decision } = yield* protocol.dispatch(copy, { local: true }, kvy);
|
|
505
|
+
if (isKramExpiry(decision))
|
|
506
|
+
throw new KramRefusedError(copy.body.said, decision);
|
|
507
|
+
}
|
|
508
|
+
yield* protocol.processEscrowsOnce();
|
|
509
|
+
}
|
|
460
510
|
/**
|
|
461
511
|
* Return the first incomplete stored `/multisig/exn` wrapping `/ipex/*`.
|
|
462
512
|
*
|
|
@@ -464,8 +464,13 @@ export class IpexV2Handler {
|
|
|
464
464
|
}
|
|
465
465
|
return { pending, registries };
|
|
466
466
|
}
|
|
467
|
-
/**
|
|
468
|
-
|
|
467
|
+
/**
|
|
468
|
+
* Notify only after Exchanger has durably accepted the parent and its evidence. A retained exchange (history a
|
|
469
|
+
* local import replayed, such as an already admitted Grant and its Admit) is not news: no notice asks anyone to act.
|
|
470
|
+
*/
|
|
471
|
+
handle({ serder, retained }) {
|
|
472
|
+
if (retained)
|
|
473
|
+
return;
|
|
469
474
|
this.notifier?.add({ r: `/exn${serder.route}`, d: serder.said, m: serder.sad.a.m });
|
|
470
475
|
}
|
|
471
476
|
/** Every disclosed own schema vets before presentation and issuer graph reduction. */
|