@interop/wallet-core 0.61.0 → 0.65.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 +41 -16
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts +46 -26
- package/dist/clientAnnex/credentialAnchoredGenesis.d.ts.map +1 -1
- package/dist/clientAnnex/credentialAnchoredGenesis.js +91 -39
- package/dist/clientAnnex/credentialAnchoredGenesis.js.map +1 -1
- package/dist/clientAnnex/establish.d.ts +55 -34
- package/dist/clientAnnex/establish.d.ts.map +1 -1
- package/dist/clientAnnex/establish.js +102 -56
- package/dist/clientAnnex/establish.js.map +1 -1
- package/dist/clientAnnex/forget.d.ts +16 -2
- package/dist/clientAnnex/forget.d.ts.map +1 -1
- package/dist/clientAnnex/forget.js +11 -9
- package/dist/clientAnnex/forget.js.map +1 -1
- package/dist/clientAnnex/forgetLast.d.ts +126 -35
- package/dist/clientAnnex/forgetLast.d.ts.map +1 -1
- package/dist/clientAnnex/forgetLast.js +208 -60
- package/dist/clientAnnex/forgetLast.js.map +1 -1
- package/dist/clientAnnex/gc.d.ts +3 -2
- package/dist/clientAnnex/gc.d.ts.map +1 -1
- package/dist/clientAnnex/gc.js +7 -7
- package/dist/clientAnnex/gc.js.map +1 -1
- package/dist/clientAnnex/heal.d.ts +101 -33
- package/dist/clientAnnex/heal.d.ts.map +1 -1
- package/dist/clientAnnex/heal.js +559 -212
- package/dist/clientAnnex/heal.js.map +1 -1
- package/dist/clientAnnex/index.d.ts +21 -5
- package/dist/clientAnnex/index.d.ts.map +1 -1
- package/dist/clientAnnex/index.js +24 -5
- package/dist/clientAnnex/index.js.map +1 -1
- package/dist/clientAnnex/ladder.d.ts +304 -18
- package/dist/clientAnnex/ladder.d.ts.map +1 -1
- package/dist/clientAnnex/ladder.js +942 -64
- package/dist/clientAnnex/ladder.js.map +1 -1
- package/dist/clientAnnex/ladderAnchored.d.ts +261 -28
- package/dist/clientAnnex/ladderAnchored.d.ts.map +1 -1
- package/dist/clientAnnex/ladderAnchored.js +521 -295
- package/dist/clientAnnex/ladderAnchored.js.map +1 -1
- package/dist/clientAnnex/log.d.ts +193 -38
- package/dist/clientAnnex/log.d.ts.map +1 -1
- package/dist/clientAnnex/log.js +445 -190
- package/dist/clientAnnex/log.js.map +1 -1
- package/dist/clientAnnex/mend.d.ts +12 -6
- package/dist/clientAnnex/mend.d.ts.map +1 -1
- package/dist/clientAnnex/mend.js +26 -12
- package/dist/clientAnnex/mend.js.map +1 -1
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts +38 -11
- package/dist/clientAnnex/recoveryLadderAnchored.d.ts.map +1 -1
- package/dist/clientAnnex/recoveryLadderAnchored.js +177 -55
- package/dist/clientAnnex/recoveryLadderAnchored.js.map +1 -1
- package/dist/clientAnnex/spaceCapability.d.ts +123 -0
- package/dist/clientAnnex/spaceCapability.d.ts.map +1 -0
- package/dist/clientAnnex/spaceCapability.js +152 -0
- package/dist/clientAnnex/spaceCapability.js.map +1 -0
- package/dist/clientAnnex/stages.d.ts +63 -0
- package/dist/clientAnnex/stages.d.ts.map +1 -0
- package/dist/clientAnnex/stages.js +64 -0
- package/dist/clientAnnex/stages.js.map +1 -0
- package/dist/clientAnnex/zcap.d.ts +1 -1
- package/dist/clientAnnex/zcap.d.ts.map +1 -1
- package/dist/clientAnnex/zcap.js +42 -35
- package/dist/clientAnnex/zcap.js.map +1 -1
- package/dist/clients/policy.d.ts +15 -1
- package/dist/clients/policy.d.ts.map +1 -1
- package/dist/clients/policy.js +12 -6
- package/dist/clients/policy.js.map +1 -1
- package/dist/clients/revocation.d.ts +18 -10
- package/dist/clients/revocation.d.ts.map +1 -1
- package/dist/clients/revocation.js +13 -5
- package/dist/clients/revocation.js.map +1 -1
- package/dist/clients/rosterPolicy.d.ts +10 -2
- package/dist/clients/rosterPolicy.d.ts.map +1 -1
- package/dist/clients/rosterPolicy.js +41 -24
- package/dist/clients/rosterPolicy.js.map +1 -1
- package/dist/descriptors/acquire.d.ts.map +1 -1
- package/dist/descriptors/acquire.js +6 -23
- package/dist/descriptors/acquire.js.map +1 -1
- package/dist/descriptors/cipher.d.ts.map +1 -1
- package/dist/descriptors/cipher.js +5 -0
- package/dist/descriptors/cipher.js.map +1 -1
- package/dist/descriptors/errors.d.ts +43 -0
- package/dist/descriptors/errors.d.ts.map +1 -0
- package/dist/descriptors/errors.js +45 -0
- package/dist/descriptors/errors.js.map +1 -0
- package/dist/descriptors/index.d.ts +5 -0
- package/dist/descriptors/index.d.ts.map +1 -1
- package/dist/descriptors/index.js +5 -0
- package/dist/descriptors/index.js.map +1 -1
- package/dist/enrollment/enrollment.d.ts +37 -10
- package/dist/enrollment/enrollment.d.ts.map +1 -1
- package/dist/enrollment/enrollment.js +52 -15
- package/dist/enrollment/enrollment.js.map +1 -1
- package/dist/genesis/accountGenesis.d.ts +42 -11
- package/dist/genesis/accountGenesis.d.ts.map +1 -1
- package/dist/genesis/accountGenesis.js +80 -22
- package/dist/genesis/accountGenesis.js.map +1 -1
- package/dist/genesis/index.d.ts +3 -1
- package/dist/genesis/index.d.ts.map +1 -1
- package/dist/genesis/index.js +3 -1
- package/dist/genesis/index.js.map +1 -1
- package/dist/identity/agents.d.ts +17 -1
- package/dist/identity/agents.d.ts.map +1 -1
- package/dist/identity/agents.js +24 -8
- package/dist/identity/agents.js.map +1 -1
- package/dist/identity/index.d.ts +3 -1
- package/dist/identity/index.d.ts.map +1 -1
- package/dist/identity/index.js +3 -1
- package/dist/identity/index.js.map +1 -1
- package/dist/index.d.ts +6 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -3
- package/dist/index.js.map +1 -1
- package/dist/keyring/index.d.ts +2 -1
- package/dist/keyring/index.d.ts.map +1 -1
- package/dist/keyring/index.js +2 -1
- package/dist/keyring/index.js.map +1 -1
- package/dist/keyring/record.d.ts.map +1 -1
- package/dist/keyring/record.js +3 -6
- package/dist/keyring/record.js.map +1 -1
- package/dist/keyring/unlockSpace.d.ts +14 -5
- package/dist/keyring/unlockSpace.d.ts.map +1 -1
- package/dist/keyring/unlockSpace.js +31 -36
- package/dist/keyring/unlockSpace.js.map +1 -1
- package/dist/keys/index.d.ts +10 -6
- package/dist/keys/index.d.ts.map +1 -1
- package/dist/keys/index.js +9 -5
- package/dist/keys/index.js.map +1 -1
- package/dist/keys/rosterLogStore.d.ts +12 -11
- package/dist/keys/rosterLogStore.d.ts.map +1 -1
- package/dist/keys/rosterLogStore.js +15 -13
- package/dist/keys/rosterLogStore.js.map +1 -1
- package/dist/keys/rosterStore.d.ts +3 -6
- package/dist/keys/rosterStore.d.ts.map +1 -1
- package/dist/keys/rosterStore.js +10 -9
- package/dist/keys/rosterStore.js.map +1 -1
- package/dist/keys/spaceEpochs.d.ts.map +1 -1
- package/dist/keys/spaceEpochs.js +2 -3
- package/dist/keys/spaceEpochs.js.map +1 -1
- package/dist/keys/userKeyRoster.d.ts +89 -9
- package/dist/keys/userKeyRoster.d.ts.map +1 -1
- package/dist/keys/userKeyRoster.js +262 -19
- package/dist/keys/userKeyRoster.js.map +1 -1
- package/dist/keys/userKeyRosterCascade.d.ts +7 -6
- package/dist/keys/userKeyRosterCascade.d.ts.map +1 -1
- package/dist/keys/userKeyRosterCascade.js +7 -6
- package/dist/keys/userKeyRosterCascade.js.map +1 -1
- package/dist/keys/wasLabelsStore.d.ts +9 -2
- package/dist/keys/wasLabelsStore.d.ts.map +1 -1
- package/dist/keys/wasLabelsStore.js +14 -6
- package/dist/keys/wasLabelsStore.js.map +1 -1
- package/dist/log.d.ts +7 -2
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +6 -1
- package/dist/log.js.map +1 -1
- package/dist/recovery/index.d.ts +12 -8
- package/dist/recovery/index.d.ts.map +1 -1
- package/dist/recovery/index.js +11 -7
- package/dist/recovery/index.js.map +1 -1
- package/dist/recovery/recoveryCode.d.ts +27 -7
- package/dist/recovery/recoveryCode.d.ts.map +1 -1
- package/dist/recovery/recoveryCode.js +18 -6
- package/dist/recovery/recoveryCode.js.map +1 -1
- package/dist/recovery/recoveryDelegation.d.ts +4 -1
- package/dist/recovery/recoveryDelegation.d.ts.map +1 -1
- package/dist/recovery/recoveryDelegation.js +25 -36
- package/dist/recovery/recoveryDelegation.js.map +1 -1
- package/dist/recovery/recoveryWebvh.d.ts +117 -65
- package/dist/recovery/recoveryWebvh.d.ts.map +1 -1
- package/dist/recovery/recoveryWebvh.js +279 -126
- package/dist/recovery/recoveryWebvh.js.map +1 -1
- package/dist/request/classify.d.ts +32 -8
- package/dist/request/classify.d.ts.map +1 -1
- package/dist/request/classify.js +39 -14
- package/dist/request/classify.js.map +1 -1
- package/dist/request/ephemeralExchange.d.ts +1 -6
- package/dist/request/ephemeralExchange.d.ts.map +1 -1
- package/dist/request/ephemeralExchange.js.map +1 -1
- package/dist/request/onboarding.d.ts.map +1 -1
- package/dist/request/onboarding.js +2 -2
- package/dist/request/onboarding.js.map +1 -1
- package/dist/request/parse.d.ts.map +1 -1
- package/dist/request/parse.js +2 -3
- package/dist/request/parse.js.map +1 -1
- package/dist/resourceLog/controller.d.ts +34 -12
- package/dist/resourceLog/controller.d.ts.map +1 -1
- package/dist/resourceLog/controller.js +79 -86
- package/dist/resourceLog/controller.js.map +1 -1
- package/dist/resourceLog/document.d.ts +182 -0
- package/dist/resourceLog/document.d.ts.map +1 -0
- package/dist/resourceLog/document.js +159 -0
- package/dist/resourceLog/document.js.map +1 -0
- package/dist/resourceLog/errors.d.ts +47 -8
- package/dist/resourceLog/errors.d.ts.map +1 -1
- package/dist/resourceLog/errors.js +54 -8
- package/dist/resourceLog/errors.js.map +1 -1
- package/dist/resourceLog/index.d.ts +7 -3
- package/dist/resourceLog/index.d.ts.map +1 -1
- package/dist/resourceLog/index.js +7 -3
- package/dist/resourceLog/index.js.map +1 -1
- package/dist/resourceLog/ladderRungs.d.ts +35 -0
- package/dist/resourceLog/ladderRungs.d.ts.map +1 -0
- package/dist/resourceLog/ladderRungs.js +352 -0
- package/dist/resourceLog/ladderRungs.js.map +1 -0
- package/dist/resourceLog/license.d.ts +42 -17
- package/dist/resourceLog/license.d.ts.map +1 -1
- package/dist/resourceLog/license.js +38 -24
- package/dist/resourceLog/license.js.map +1 -1
- package/dist/space/activity.d.ts +15 -15
- package/dist/space/activity.d.ts.map +1 -1
- package/dist/space/activity.js +15 -15
- package/dist/space/activity.js.map +1 -1
- package/dist/space/collections.d.ts +11 -0
- package/dist/space/collections.d.ts.map +1 -1
- package/dist/space/collections.js +13 -0
- package/dist/space/collections.js.map +1 -1
- package/dist/space/deleteSpace.d.ts +28 -0
- package/dist/space/deleteSpace.d.ts.map +1 -0
- package/dist/space/deleteSpace.js +44 -0
- package/dist/space/deleteSpace.js.map +1 -0
- package/dist/space/errors.d.ts.map +1 -1
- package/dist/space/errors.js +0 -1
- package/dist/space/errors.js.map +1 -1
- package/dist/space/index.d.ts +6 -0
- package/dist/space/index.d.ts.map +1 -1
- package/dist/space/index.js +6 -0
- package/dist/space/index.js.map +1 -1
- package/dist/space/plaintextCollection.d.ts +43 -0
- package/dist/space/plaintextCollection.d.ts.map +1 -0
- package/dist/space/plaintextCollection.js +17 -0
- package/dist/space/plaintextCollection.js.map +1 -0
- package/dist/stages.d.ts +23 -0
- package/dist/stages.d.ts.map +1 -0
- package/dist/stages.js +23 -0
- package/dist/stages.js.map +1 -0
- package/dist/sync/index.d.ts +7 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +7 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/push.js +4 -4
- package/dist/sync/push.js.map +1 -1
- package/dist/sync/remint.js +2 -2
- package/dist/sync/remint.js.map +1 -1
- package/dist/sync/types.d.ts +41 -1
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/types.js +47 -1
- package/dist/sync/types.js.map +1 -1
- package/dist/unlock/index.d.ts +6 -2
- package/dist/unlock/index.d.ts.map +1 -1
- package/dist/unlock/index.js +5 -1
- package/dist/unlock/index.js.map +1 -1
- package/dist/unlock/retire.d.ts +95 -14
- package/dist/unlock/retire.d.ts.map +1 -1
- package/dist/unlock/retire.js +102 -10
- package/dist/unlock/retire.js.map +1 -1
- package/dist/unlock/standingClient.d.ts.map +1 -1
- package/dist/unlock/standingClient.js +5 -1
- package/dist/unlock/standingClient.js.map +1 -1
- package/dist/unlock/standingWebvh.d.ts +341 -49
- package/dist/unlock/standingWebvh.d.ts.map +1 -1
- package/dist/unlock/standingWebvh.js +608 -164
- package/dist/unlock/standingWebvh.js.map +1 -1
- package/dist/webvh/accountEntry.d.ts +146 -0
- package/dist/webvh/accountEntry.d.ts.map +1 -0
- package/dist/webvh/accountEntry.js +239 -0
- package/dist/webvh/accountEntry.js.map +1 -0
- package/dist/webvh/didWeb.d.ts +12 -7
- package/dist/webvh/didWeb.d.ts.map +1 -1
- package/dist/webvh/didWeb.js +2 -2
- package/dist/webvh/didWeb.js.map +1 -1
- package/dist/webvh/didWebProjection.d.ts +164 -0
- package/dist/webvh/didWebProjection.d.ts.map +1 -0
- package/dist/webvh/didWebProjection.js +230 -0
- package/dist/webvh/didWebProjection.js.map +1 -0
- package/dist/webvh/didWebvh.d.ts +278 -138
- package/dist/webvh/didWebvh.d.ts.map +1 -1
- package/dist/webvh/didWebvh.js +314 -345
- package/dist/webvh/didWebvh.js.map +1 -1
- package/dist/webvh/enrollClient.d.ts +65 -0
- package/dist/webvh/enrollClient.d.ts.map +1 -0
- package/dist/webvh/enrollClient.js +172 -0
- package/dist/webvh/enrollClient.js.map +1 -0
- package/dist/webvh/index.d.ts +37 -12
- package/dist/webvh/index.d.ts.map +1 -1
- package/dist/webvh/index.js +33 -10
- package/dist/webvh/index.js.map +1 -1
- package/dist/webvh/listClients.d.ts +1 -33
- package/dist/webvh/listClients.d.ts.map +1 -1
- package/dist/webvh/listClients.js +2 -28
- package/dist/webvh/listClients.js.map +1 -1
- package/dist/webvh/revokeClient.d.ts +89 -11
- package/dist/webvh/revokeClient.d.ts.map +1 -1
- package/dist/webvh/revokeClient.js +182 -47
- package/dist/webvh/revokeClient.js.map +1 -1
- package/dist/webvh/standingZcap.d.ts +61 -14
- package/dist/webvh/standingZcap.d.ts.map +1 -1
- package/dist/webvh/standingZcap.js +91 -0
- package/dist/webvh/standingZcap.js.map +1 -1
- package/dist/webvh/verifyLog.d.ts +59 -5
- package/dist/webvh/verifyLog.d.ts.map +1 -1
- package/dist/webvh/verifyLog.js +76 -11
- package/dist/webvh/verifyLog.js.map +1 -1
- package/dist/webvh/wasIdStore.d.ts +8 -8
- package/dist/webvh/wasIdStore.d.ts.map +1 -1
- package/dist/webvh/wasIdStore.js +39 -14
- package/dist/webvh/wasIdStore.js.map +1 -1
- package/dist/webvh/zcap.d.ts +14 -1
- package/dist/webvh/zcap.d.ts.map +1 -1
- package/dist/webvh/zcap.js +3 -27
- package/dist/webvh/zcap.js.map +1 -1
- package/package.json +5 -5
- package/dist/webvh/keyAgreement.d.ts +0 -72
- package/dist/webvh/keyAgreement.d.ts.map +0 -1
- package/dist/webvh/keyAgreement.js +0 -47
- package/dist/webvh/keyAgreement.js.map +0 -1
|
@@ -25,22 +25,26 @@
|
|
|
25
25
|
* hash. The credential's own inventory -- its `keyAgreement` entry and the
|
|
26
26
|
* freshly committed `hash(rung i + 1)` -- stands untouched, ready for
|
|
27
27
|
* the next self-enrollment. Nothing is spent, and no replacement exists.
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
28
|
+
* Every standing credential's ladder VM is left alone: a VM's life is
|
|
29
|
+
* keyed to its credential rather than to the account's client census, so
|
|
30
|
+
* it stands alongside the client this entry publishes.
|
|
31
|
+
*
|
|
32
|
+
* - {@link revealLadderRungWebvh} -- the reveal-and-commit entry on its own,
|
|
33
|
+
* for a credential-only visit that needs to sign one account-log entry
|
|
34
|
+
* after a self-enrollment spent the previously revealed rung.
|
|
32
35
|
*
|
|
33
36
|
* - {@link forgetWebvhClient} -- self-enrollment in reverse: one atomic
|
|
34
37
|
* ladder-signed removal entry through the bridge takes an enrolled client's
|
|
35
38
|
* whole document inventory out; the last enrolled client refuses
|
|
36
39
|
* ({@link LastEnrolledClientForgetError}).
|
|
37
|
-
* - {@link
|
|
38
|
-
* entries of the LAST enrolled
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
40
|
+
* - {@link strikeLadderVmWebvh} / {@link installLadderVmWebvh} /
|
|
41
|
+
* `forgetLastWebvhClient` -- the entries of the LAST enrolled
|
|
42
|
+
* client's forget (decision 0004's 2026-08-19 amendment): the credential's
|
|
43
|
+
* own ladder VM struck and then reinstalled while the client stands (the
|
|
44
|
+
* pair supplying the transition's inventory-changing document version),
|
|
45
|
+
* then -- after the revocations the composed ceremony runs in between -- a
|
|
46
|
+
* removal entry that takes the client out while the reinstalled VM keeps
|
|
47
|
+
* the account ladder-anchored. The composed ceremony is `forgetLast.ts`.
|
|
44
48
|
*
|
|
45
49
|
* Which rung is current is recovered from the log itself
|
|
46
50
|
* (`attributeLadderRung`, fail-closed); a lost compare-and-swap race re-runs,
|
|
@@ -49,12 +53,14 @@
|
|
|
49
53
|
* from durable state alone, on the recovery continuation's pattern.
|
|
50
54
|
*/
|
|
51
55
|
import { deriveNextKeyHash, updateDID } from '@interop/did-method-webvh';
|
|
52
|
-
import { assertCanonicalClientKeys,
|
|
56
|
+
import { assertCanonicalClientKeys, backfillKeyMapWebvhBlock, concludeWithPublishedLog, createLadderAnchoredWebvhLog, didWebvhControllerTemplate, genesisNextKeyHashes, ladderVerificationMethod, markedVerificationMethodPair, pinOfLog, publishEntryPinned, publishWebvhLog, readPublishedLog, readPublishedLogOrThrow, servedHead, updateKeySigner, withLogConflictRetry, writeKeysJson } from '../webvh/didWebvh.js';
|
|
57
|
+
import { putDidWebProjection } from '../webvh/didWebProjection.js';
|
|
53
58
|
import { accountLogPinId } from '../webvh/verifyLog.js';
|
|
54
|
-
import { ladderVmIds } from '../
|
|
59
|
+
import { ladderVmIds, relationIds } from '../resourceLog/document.js';
|
|
55
60
|
import { clientRemovalFields, clientRemovalTarget } from '../webvh/revokeClient.js';
|
|
56
|
-
import {
|
|
61
|
+
import { unlockKeyVerificationMethod } from '../unlock/standingWebvh.js';
|
|
57
62
|
import { attributeLadderRung, ladderRung, ladderVmKeyMultibase } from './ladder.js';
|
|
63
|
+
import { signAccountEntry } from '../webvh/accountEntry.js';
|
|
58
64
|
/**
|
|
59
65
|
* LADDER-ANCHORED GENESIS: assembles the one-entry did:webvh log of an account
|
|
60
66
|
* with zero enrolled clients, anchored on the minting credential's
|
|
@@ -69,9 +75,10 @@ import { attributeLadderRung, ladderRung, ladderVmKeyMultibase } from './ladder.
|
|
|
69
75
|
* separate bind entry ({@link publishUnlockKey}) -- so the genesis
|
|
70
76
|
* `keyAgreement` array holds only the credential's entry.
|
|
71
77
|
*
|
|
72
|
-
* The ladder-anchored
|
|
73
|
-
*
|
|
74
|
-
*
|
|
78
|
+
* The account stays ladder-anchored until a self-enrollment
|
|
79
|
+
* ({@link selfEnrollWebvhClient}) publishes a client and retires rung 0. That
|
|
80
|
+
* entry leaves the ladder VM standing: it is struck when the credential
|
|
81
|
+
* retires, not when a client arrives.
|
|
75
82
|
*
|
|
76
83
|
* When the wallet keeps a KMS, `didWebKeys` folds the KMS-held
|
|
77
84
|
* authentication key into the entry under `authentication` only -- the
|
|
@@ -89,7 +96,7 @@ import { attributeLadderRung, ladderRung, ladderVmKeyMultibase } from './ladder.
|
|
|
89
96
|
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
90
97
|
* @param options.keyAgreement {UnlockKeyAgreementPublication} the
|
|
91
98
|
* credential's key-agreement publication (commitment or verbatim)
|
|
92
|
-
* @returns {Promise<
|
|
99
|
+
* @returns {Promise<CreatedWebvhLog>}
|
|
93
100
|
*/
|
|
94
101
|
export async function createLadderAnchoredAccountLog({ wasServerUrl, spaceId, didWebKeys, ladderSeed, keyAgreement }) {
|
|
95
102
|
const rung0 = await ladderRung({ ladderSeed, index: 0 });
|
|
@@ -140,6 +147,9 @@ export async function createLadderAnchoredAccountLog({ wasServerUrl, spaceId, di
|
|
|
140
147
|
* the wallet keeps a KMS; folded into the CREATE path only (see the
|
|
141
148
|
* adoption note in the body), which also records the minted DID into
|
|
142
149
|
* keys.json's webvh block as the enrolled-client ensure does
|
|
150
|
+
* @param [options.keysJsonEtag] {string} the ETag the KMS-authentication
|
|
151
|
+
* stage's own `keys.json` write returned, carried as the `ifMatch` of that
|
|
152
|
+
* rewrite
|
|
143
153
|
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
144
154
|
* @param options.keyAgreement {UnlockKeyAgreementPublication} the
|
|
145
155
|
* credential's key-agreement publication (commitment or verbatim)
|
|
@@ -149,7 +159,18 @@ export async function createLadderAnchoredAccountLog({ wasServerUrl, spaceId, di
|
|
|
149
159
|
* @param [options.pinStore] {ResourceLogPinStore} this client's chain-head
|
|
150
160
|
* pins; the account log's slot is keyed by `accountLogPinId` over the
|
|
151
161
|
* `spaceId` above
|
|
152
|
-
* @returns {Promise<{ did: string
|
|
162
|
+
* @returns {Promise<{ did: string, published: PublishedWebvhLog,
|
|
163
|
+
* logMinted: boolean }>} `published` is the head this stage stands on --
|
|
164
|
+
* the served one on the adopt branch, the minted one paired with its
|
|
165
|
+
* create PUT's ETag on the create branch -- so the stage after it can build
|
|
166
|
+
* on this head instead of re-reading the log this one just read or wrote.
|
|
167
|
+
* Its `etag` is absent against a backend that serves none, which is one
|
|
168
|
+
* case a later stage must still read for itself. `logMinted` says WHICH
|
|
169
|
+
* branch produced it, which a later stage needs before reusing it: a
|
|
170
|
+
* minted head is one no other writer could have held a moment ago, while
|
|
171
|
+
* an adopted head is a snapshot of an account other clients are free to
|
|
172
|
+
* write to, and the parts of it no ETag protects (the document's
|
|
173
|
+
* completion tests) can be stale by the time a later stage reads them
|
|
153
174
|
*/
|
|
154
175
|
export async function ensureLadderAnchoredDidWebvh(options) {
|
|
155
176
|
return withLogConflictRetry(() => ensureLadderAnchoredDidWebvhOnce(options));
|
|
@@ -159,9 +180,10 @@ export async function ensureLadderAnchoredDidWebvh(options) {
|
|
|
159
180
|
* conflict retry.
|
|
160
181
|
*
|
|
161
182
|
* @param options {object} see {@link ensureLadderAnchoredDidWebvh}
|
|
162
|
-
* @returns {Promise<{ did: string
|
|
183
|
+
* @returns {Promise<{ did: string, published: PublishedWebvhLog,
|
|
184
|
+
* logMinted: boolean }>}
|
|
163
185
|
*/
|
|
164
|
-
async function ensureLadderAnchoredDidWebvhOnce({ idStore, wasServerUrl, spaceId, didWebKeys, ladderSeed, keyAgreement, expectedDid, pinStore }) {
|
|
186
|
+
async function ensureLadderAnchoredDidWebvhOnce({ idStore, wasServerUrl, spaceId, didWebKeys, keysJsonEtag, ladderSeed, keyAgreement, expectedDid, pinStore }) {
|
|
165
187
|
const logId = accountLogPinId({ spaceId });
|
|
166
188
|
const published = await readPublishedLog({
|
|
167
189
|
idStore,
|
|
@@ -179,9 +201,21 @@ async function ensureLadderAnchoredDidWebvhOnce({ idStore, wasServerUrl, spaceId
|
|
|
179
201
|
// edits it, and a log published without the KMS convenience key is
|
|
180
202
|
// healed by a later login, not here.
|
|
181
203
|
await attributeLadderRung({ ladderSeed, published });
|
|
182
|
-
// Heals a
|
|
204
|
+
// Heals a keys.json left without its `webvh` block by a run torn between
|
|
205
|
+
// the genesis entry and the rewrite: the served map's own binding gains
|
|
206
|
+
// the DID this log resolves to. Gated on this run keeping a KMS, so a
|
|
207
|
+
// KMS-less wallet spends no read on a resource it never writes.
|
|
208
|
+
if (didWebKeys) {
|
|
209
|
+
await backfillKeyMapWebvhBlock({ idStore, did: published.did });
|
|
210
|
+
}
|
|
211
|
+
// Heals a did.json left lagging by a torn earlier publish of this
|
|
212
|
+
// controller-invoking genesis; a lag left by a later ladder-signed entry
|
|
213
|
+
// is `ensureDidWebProjection`'s to mend.
|
|
183
214
|
const { did } = await concludeWithPublishedLog({ idStore, published });
|
|
184
|
-
|
|
215
|
+
// The served head verbatim: the projection PUT above touches no log, so
|
|
216
|
+
// the read's own ETag is still the log's validator. `logMinted: false`
|
|
217
|
+
// marks it as a snapshot of an account other clients may be writing to.
|
|
218
|
+
return { did, published, logMinted: false };
|
|
185
219
|
}
|
|
186
220
|
const created = await createLadderAnchoredAccountLog({
|
|
187
221
|
wasServerUrl,
|
|
@@ -190,7 +224,7 @@ async function ensureLadderAnchoredDidWebvhOnce({ idStore, wasServerUrl, spaceId
|
|
|
190
224
|
ladderSeed,
|
|
191
225
|
keyAgreement
|
|
192
226
|
});
|
|
193
|
-
await publishWebvhLog({
|
|
227
|
+
const written = await publishWebvhLog({
|
|
194
228
|
idStore,
|
|
195
229
|
log: created.log,
|
|
196
230
|
webDoc: created.webDoc,
|
|
@@ -211,26 +245,86 @@ async function ensureLadderAnchoredDidWebvhOnce({ idStore, wasServerUrl, spaceId
|
|
|
211
245
|
await writeKeysJson({
|
|
212
246
|
idStore,
|
|
213
247
|
didWebKeys,
|
|
214
|
-
webvh: { did: created.did }
|
|
248
|
+
webvh: { did: created.did },
|
|
249
|
+
...(keysJsonEtag !== undefined && { ifMatch: keysJsonEtag })
|
|
215
250
|
});
|
|
216
251
|
}
|
|
217
|
-
|
|
252
|
+
// The head this run just wrote, assembled from what `createDID` already
|
|
253
|
+
// resolved plus the create PUT's own validator -- no second read, and no
|
|
254
|
+
// second resolve.
|
|
255
|
+
return {
|
|
256
|
+
did: created.did,
|
|
257
|
+
logMinted: true,
|
|
258
|
+
published: {
|
|
259
|
+
log: created.log,
|
|
260
|
+
did: created.did,
|
|
261
|
+
doc: created.doc,
|
|
262
|
+
updateKeys: created.updateKeys,
|
|
263
|
+
nextKeyHashes: created.nextKeyHashes,
|
|
264
|
+
...(written.etag !== undefined ? { etag: written.etag } : {})
|
|
265
|
+
}
|
|
266
|
+
};
|
|
218
267
|
}
|
|
219
268
|
/**
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
269
|
+
* ONE LADDER-SIGNED ACCOUNT-LOG ENTRY: the account-entry seam
|
|
270
|
+
* ({@link signAccountEntry}) on its ladder arm, narrowed to the outcome the
|
|
271
|
+
* annex's ceremonies read. The seam owns the nine steps -- the pinned read,
|
|
272
|
+
* the rung attribution, the rung's carry-over hash, the carry-over
|
|
273
|
+
* precondition, the update-key signer, the self-reveal union into
|
|
274
|
+
* `updateKeys`, the carry-over union into `nextKeyHashes`, the conditional
|
|
275
|
+
* publish (`did.jsonl` alone, the bridge's whole reach), and the pin advance.
|
|
276
|
+
*
|
|
277
|
+
* No conflict retry of its own: a lost compare-and-swap surfaces as a
|
|
278
|
+
* `WebvhLogConflictError` for the caller's {@link withLogConflictRetry} to
|
|
279
|
+
* re-run, which is what re-attributes the rung and climbs to the winner's
|
|
280
|
+
* committed one (the retry-up-the-ladder resolution).
|
|
225
281
|
*
|
|
226
282
|
* @param options {object}
|
|
227
|
-
* @param options.store {UnlockLogStore}
|
|
228
|
-
* @param options.
|
|
229
|
-
* @param [options.
|
|
230
|
-
*
|
|
283
|
+
* @param options.store {UnlockLogStore} public log read + delegated PUT
|
|
284
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
285
|
+
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
286
|
+
* to, from the caller's stored account pointer
|
|
287
|
+
* @param [options.pinStore] {ResourceLogPinStore} the caller's chain-head
|
|
288
|
+
* pins: the read is checked against the pinned head, and the pin advances
|
|
289
|
+
* to the head this entry publishes
|
|
290
|
+
* @param [options.logId] {string} the account log's pin slot
|
|
291
|
+
* (`accountLogPinId({ spaceId })`); required whenever a `pinStore` is
|
|
292
|
+
* supplied
|
|
293
|
+
* @param [options.skip] {function} `(published) => boolean` -- run on the
|
|
294
|
+
* read, before any attribution; `true` returns `skipped` with nothing
|
|
295
|
+
* published
|
|
296
|
+
* @param options.build {function}
|
|
297
|
+
* `({ published, rung, state }) => LadderSignedEntry | undefined` -- the
|
|
298
|
+
* entry's own members, or `undefined` to decline
|
|
299
|
+
* @param [options.beforePublish] {function} `({ updated }) => Promise<void>`
|
|
300
|
+
* -- the pre-publish seam, for the ceremonies that PUT their post-entry
|
|
301
|
+
* `did:web` projection while the authority they are about to end can still
|
|
302
|
+
* write it. See {@link signAccountEntry}
|
|
303
|
+
* @returns {Promise<LadderSignedEntryOutcome>}
|
|
231
304
|
*/
|
|
232
|
-
async function
|
|
233
|
-
await
|
|
305
|
+
export async function ladderSignedAccountEntry({ store, ladderSeed, expectedDid, pinStore, logId, skip, build, beforePublish }) {
|
|
306
|
+
const outcome = await signAccountEntry({
|
|
307
|
+
idStore: store,
|
|
308
|
+
signer: { kind: 'ladder', ladderSeed },
|
|
309
|
+
build: ({ published, rung, state }) => build({ published, rung: rung, state: state }),
|
|
310
|
+
...(skip ? { skip } : {}),
|
|
311
|
+
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
312
|
+
...(pinStore ? { pinStore } : {}),
|
|
313
|
+
...(logId !== undefined ? { logId } : {}),
|
|
314
|
+
missingMessage: 'did:webvh: did.jsonl is missing; nothing to enroll into.',
|
|
315
|
+
...(beforePublish ? { beforePublish } : {})
|
|
316
|
+
});
|
|
317
|
+
if (outcome.skipped) {
|
|
318
|
+
return { skipped: true, published: outcome.published };
|
|
319
|
+
}
|
|
320
|
+
return {
|
|
321
|
+
skipped: false,
|
|
322
|
+
published: outcome.published,
|
|
323
|
+
rung: outcome.rung,
|
|
324
|
+
rungHash: outcome.rungHash,
|
|
325
|
+
state: outcome.state,
|
|
326
|
+
...(outcome.updated ? { updated: outcome.updated } : {})
|
|
327
|
+
};
|
|
234
328
|
}
|
|
235
329
|
/**
|
|
236
330
|
* SELF-ENROLLMENT (run by the credential-derived client through the delegated
|
|
@@ -401,83 +495,63 @@ export class BuiltOnHeadNotReachedError extends Error {
|
|
|
401
495
|
* @returns {Promise<{ did: string, webDoc?: object, committed: boolean }>}
|
|
402
496
|
*/
|
|
403
497
|
async function selfEnrollWebvhClientOnce({ store, ladderSeed, newClientKeys, newClientUpdateSeeds, onCommitted, builtOnHead, expectedDid, pinStore, logId }) {
|
|
404
|
-
//
|
|
405
|
-
//
|
|
406
|
-
//
|
|
407
|
-
const
|
|
408
|
-
...(pinStore ? { pinStore } : {}),
|
|
409
|
-
...(logId !== undefined ? { logId } : {})
|
|
410
|
-
};
|
|
411
|
-
let published = await readLogOrThrow({
|
|
498
|
+
// The reveal-and-commit entry, through the shared preamble and postamble.
|
|
499
|
+
// It is skipped when a torn earlier run already published it (the rung
|
|
500
|
+
// revealed AND every needed hash committed).
|
|
501
|
+
const reveal = await ladderSignedAccountEntry({
|
|
412
502
|
store,
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
const updated = await updateDID({
|
|
450
|
-
log: published.log,
|
|
451
|
-
signer,
|
|
452
|
-
alsoKnownAsWeb: true,
|
|
453
|
-
updateKeys: [...new Set([...published.updateKeys, rung.keyMultibase])],
|
|
454
|
-
// The spent rung's own hash is kept through this entry (so a resumed
|
|
455
|
-
// commit can re-state the revealed key); the add entry drops it, while
|
|
456
|
-
// the next rung's hash stays as the credential's standing commitment.
|
|
457
|
-
nextKeyHashes: [
|
|
458
|
-
...new Set([
|
|
459
|
-
...published.nextKeyHashes,
|
|
460
|
-
rungHash,
|
|
461
|
-
newUpdateHash,
|
|
462
|
-
newStagedHash,
|
|
463
|
-
nextRungHash
|
|
464
|
-
])
|
|
465
|
-
]
|
|
466
|
-
});
|
|
467
|
-
await publishLogOnly({ store, log: updated.log, ifMatch: published.etag });
|
|
468
|
-
// Advance the pin to what the reveal entry just published, so the re-read
|
|
469
|
-
// below (and any read after a tear here) refuses a host that rolls the
|
|
470
|
-
// log back behind it.
|
|
471
|
-
if (pinStore && logId !== undefined) {
|
|
472
|
-
await pinStore.write({ logId, pin: pinOfLog(updated.log) });
|
|
503
|
+
ladderSeed,
|
|
504
|
+
expectedDid,
|
|
505
|
+
pinStore,
|
|
506
|
+
logId,
|
|
507
|
+
skip: read => {
|
|
508
|
+
// The resume marker, checked before anything else -- the completion
|
|
509
|
+
// check included, so a truncated served log is refused rather than read
|
|
510
|
+
// as "not complete yet" and rebuilt over.
|
|
511
|
+
if (builtOnHead) {
|
|
512
|
+
const genesisScid = read.log[0]?.parameters.scid ?? '';
|
|
513
|
+
const reached = read.log.some(entry => entry.versionId === builtOnHead.versionId);
|
|
514
|
+
if (genesisScid !== builtOnHead.scid || !reached) {
|
|
515
|
+
throw new BuiltOnHeadNotReachedError({ builtOnHead });
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
// Already complete (a torn earlier run finished the add entry): the new
|
|
519
|
+
// client's update key is authorized, which only the add entry writes.
|
|
520
|
+
// The seam is deliberately NOT entered on this path -- nothing is about
|
|
521
|
+
// to be published, so there is no pivot to persist ahead of.
|
|
522
|
+
return read.updateKeys.includes(newClientKeys.updateKeyMultibase);
|
|
523
|
+
},
|
|
524
|
+
build: async ({ published: read, rung, state }) => {
|
|
525
|
+
const nextRung = await ladderRung({ ladderSeed, index: rung.index + 1 });
|
|
526
|
+
const newUpdateHash = await deriveNextKeyHash(newClientKeys.updateKeyMultibase);
|
|
527
|
+
const newStagedHash = await deriveNextKeyHash(newClientKeys.stagedUpdateKeyMultibase);
|
|
528
|
+
const nextRungHash = await deriveNextKeyHash(nextRung.keyMultibase);
|
|
529
|
+
const committed = [newUpdateHash, newStagedHash, nextRungHash].every(hash => read.nextKeyHashes.includes(hash));
|
|
530
|
+
if (state === 'revealed' && committed) {
|
|
531
|
+
return undefined;
|
|
532
|
+
}
|
|
533
|
+
// The spent rung's own hash is kept through this entry by the shared
|
|
534
|
+
// carry-over union (so a resumed commit can re-state the revealed key);
|
|
535
|
+
// the add entry drops it, while the next rung's hash stays as the
|
|
536
|
+
// credential's standing commitment. The three land after it, in the
|
|
537
|
+
// append order `decisions/0007` ratifies.
|
|
538
|
+
return { commitHashes: [newUpdateHash, newStagedHash, nextRungHash] };
|
|
473
539
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
expectedDid: published.did,
|
|
478
|
-
...pinned
|
|
479
|
-
});
|
|
540
|
+
});
|
|
541
|
+
if (reveal.skipped) {
|
|
542
|
+
return { did: reveal.published.did, committed: false };
|
|
480
543
|
}
|
|
544
|
+
const { rung, rungHash } = reveal;
|
|
545
|
+
// The same account the reveal entry just extended, under the same pin.
|
|
546
|
+
const published = reveal.updated
|
|
547
|
+
? await readPublishedLogOrThrow({
|
|
548
|
+
idStore: store,
|
|
549
|
+
expectedDid: reveal.published.did,
|
|
550
|
+
pinStore,
|
|
551
|
+
logId,
|
|
552
|
+
missingMessage: 'did:webvh: did.jsonl is missing; nothing to enroll into.'
|
|
553
|
+
})
|
|
554
|
+
: reveal.published;
|
|
481
555
|
// The persist-before-publish seam: the pending client-key record is
|
|
482
556
|
// persisted client-local HERE, on the head the add entry is about to be
|
|
483
557
|
// built on, before that entry -- the ceremony's pivot -- publishes a client
|
|
@@ -491,13 +565,11 @@ async function selfEnrollWebvhClientOnce({ store, ladderSeed, newClientKeys, new
|
|
|
491
565
|
// client's update key, whose hash the commit entry just committed.
|
|
492
566
|
const { did, doc } = published;
|
|
493
567
|
const vmId = (publicKeyMultibase) => `${did}#${publicKeyMultibase}`;
|
|
494
|
-
//
|
|
495
|
-
//
|
|
496
|
-
//
|
|
497
|
-
//
|
|
498
|
-
//
|
|
499
|
-
// finds none and the filters are no-ops.
|
|
500
|
-
const ladderVms = ladderVmIds({ doc });
|
|
568
|
+
// Enrollment does not touch any ladder VM. A ladder VM's life is keyed to
|
|
569
|
+
// its credential: the standing establishment installs it, the credential's
|
|
570
|
+
// retirement strikes it, and every standing credential's VM stays in the
|
|
571
|
+
// document alongside the enrolled clients this entry publishes. The only
|
|
572
|
+
// ladder state this entry changes is the spent rung's, below.
|
|
501
573
|
const addedMethods = markedVerificationMethodPair({
|
|
502
574
|
controller: did,
|
|
503
575
|
signingKeyMultibase: newClientKeys.signingKeyMultibase,
|
|
@@ -505,11 +577,10 @@ async function selfEnrollWebvhClientOnce({ store, ladderSeed, newClientKeys, new
|
|
|
505
577
|
});
|
|
506
578
|
const existingMethods = (doc.verificationMethod ?? []);
|
|
507
579
|
const verificationMethods = [
|
|
508
|
-
...existingMethods.filter(method => !addedMethods.some(added => added.id === method.id)
|
|
509
|
-
(method.id === undefined || !ladderVms.includes(method.id))),
|
|
580
|
+
...existingMethods.filter(method => !addedMethods.some(added => added.id === method.id)),
|
|
510
581
|
...addedMethods
|
|
511
582
|
];
|
|
512
|
-
const withReference = (relation, id) => [...new Set([...relationIds(relation), id])]
|
|
583
|
+
const withReference = (relation, id) => [...new Set([...relationIds(relation), id])];
|
|
513
584
|
const signingVmId = vmId(newClientKeys.signingKeyMultibase);
|
|
514
585
|
const signer = await updateKeySigner({
|
|
515
586
|
seed: newClientUpdateSeeds.updateSeed
|
|
@@ -534,12 +605,13 @@ async function selfEnrollWebvhClientOnce({ store, ladderSeed, newClientKeys, new
|
|
|
534
605
|
});
|
|
535
606
|
// Conditional on the read this entry was built on: the re-read above when
|
|
536
607
|
// the commit entry ran here, the first read when it was skipped.
|
|
537
|
-
await
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
608
|
+
await publishEntryPinned({
|
|
609
|
+
store,
|
|
610
|
+
log: updated.log,
|
|
611
|
+
ifMatch: published.etag,
|
|
612
|
+
pinStore,
|
|
613
|
+
logId
|
|
614
|
+
});
|
|
543
615
|
return { did: updated.did, webDoc: updated.webDoc, committed: true };
|
|
544
616
|
}
|
|
545
617
|
/**
|
|
@@ -603,6 +675,13 @@ export class LastEnrolledClientForgetError extends Error {
|
|
|
603
675
|
* @param options {object}
|
|
604
676
|
* @param options.store {UnlockLogStore} the credential's delegated
|
|
605
677
|
* `did.jsonl` bridge store
|
|
678
|
+
* @param [options.projectionStore] {object} an `id`-collection store the
|
|
679
|
+
* FORGETTING client can still write through (its own root-invoking store):
|
|
680
|
+
* the post-removal `did:web` projection is PUT through it immediately
|
|
681
|
+
* before the removal entry publishes, and only when this run publishes that
|
|
682
|
+
* entry. See {@link clientForgetEntryOnce} for the ordering rationale.
|
|
683
|
+
* Omitted, `did.json` keeps naming the forgotten client until some later
|
|
684
|
+
* writer runs `ensureDidWebProjection`
|
|
606
685
|
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
607
686
|
* @param options.forgottenClient {RevokedClientKeys} this client's public
|
|
608
687
|
* halves; an `updateKeyMultibase` the log does not authorize (stale, or the
|
|
@@ -624,127 +703,139 @@ export class LastEnrolledClientForgetError extends Error {
|
|
|
624
703
|
* on the idempotent no-op path)
|
|
625
704
|
*/
|
|
626
705
|
export async function forgetWebvhClient(options) {
|
|
627
|
-
return withLogConflictRetry(() => clientForgetEntryOnce({ ...options,
|
|
706
|
+
return withLogConflictRetry(() => clientForgetEntryOnce({ ...options, assertRemovable: assertNotLastClient }));
|
|
628
707
|
}
|
|
629
708
|
/**
|
|
630
|
-
*
|
|
631
|
-
*
|
|
632
|
-
*
|
|
633
|
-
* the
|
|
634
|
-
*
|
|
635
|
-
*
|
|
636
|
-
*
|
|
637
|
-
*
|
|
638
|
-
*
|
|
639
|
-
*
|
|
640
|
-
*
|
|
641
|
-
* @
|
|
642
|
-
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog }>}
|
|
709
|
+
* The plain forget's removability invariant: `capabilityInvocation` lists
|
|
710
|
+
* exactly the enrolled clients' signing keys (a recovery code's key is
|
|
711
|
+
* `keyAgreement`-only and the KMS convenience key `authentication`-only), so
|
|
712
|
+
* the forgotten client standing alone there means removing it strands the
|
|
713
|
+
* account. The transition ceremony supplies its own invariant instead
|
|
714
|
+
* (`forgetLast.ts`), which is why this one is injected rather than selected
|
|
715
|
+
* by a flag inside the shared entry builder.
|
|
716
|
+
*
|
|
717
|
+
* @param options {object}
|
|
718
|
+
* @param options.published {PublishedWebvhLog}
|
|
719
|
+
* @param options.target {ClientRemovalTarget}
|
|
720
|
+
* @returns {void}
|
|
643
721
|
*/
|
|
644
|
-
|
|
645
|
-
|
|
722
|
+
function assertNotLastClient({ published, target }) {
|
|
723
|
+
const invocationIds = relationIds(published.doc.capabilityInvocation);
|
|
724
|
+
if (invocationIds.includes(target.signingVmId) &&
|
|
725
|
+
invocationIds.every(id => id === target.signingVmId)) {
|
|
726
|
+
throw new LastEnrolledClientForgetError();
|
|
727
|
+
}
|
|
646
728
|
}
|
|
647
729
|
/**
|
|
648
|
-
* One attempt of {@link forgetWebvhClient} or
|
|
649
|
-
* re-invoked by
|
|
650
|
-
*
|
|
651
|
-
*
|
|
730
|
+
* One attempt of {@link forgetWebvhClient} or `forgetLastWebvhClient`
|
|
731
|
+
* (`forgetLast.ts`), re-invoked by their conflict retries. The two share the
|
|
732
|
+
* whole removal entry and differ only in what makes the removal admissible,
|
|
733
|
+
* which each supplies as `assertRemovable` -- the plain forget refuses the
|
|
734
|
+
* last enrolled client, the transition removal requires the ladder VM already
|
|
735
|
+
* installed. The invariant travels with the ceremony that owns it rather than
|
|
736
|
+
* living here as a flag.
|
|
737
|
+
*
|
|
738
|
+
* THE PROJECTION IS PUBLISHED BEFORE THE ENTRY, and the order is forced: the
|
|
739
|
+
* removal entry is ladder-signed and publishes `did.jsonl` alone (the bridge
|
|
740
|
+
* delegation covers nothing else), while the forgotten client's authority
|
|
741
|
+
* dies at that entry under the current-key-set rule. So the post-removal
|
|
742
|
+
* `did:web` projection has to be written while that client can still write
|
|
743
|
+
* it. A run torn between the projection PUT and the entry leaves `did.json`
|
|
744
|
+
* omitting a client the log still lists, which is fail-closed for a `did:web`
|
|
745
|
+
* verifier and is re-PUT by the re-run; the reverse order would leave the
|
|
746
|
+
* revoked client standing in `did.json` with nothing left able to remove it.
|
|
747
|
+
* The idempotent already-forgotten path writes no projection at all: the
|
|
748
|
+
* removal entry landed on an earlier run, so this client's authority is
|
|
749
|
+
* already gone and its store can only be refused. A projection that path
|
|
750
|
+
* leaves stale is mended by the next transient visit's
|
|
751
|
+
* `ensureDidWebProjection`, which invokes under its generation delegation.
|
|
652
752
|
*
|
|
653
753
|
* @param options {object} see {@link forgetWebvhClient}, plus:
|
|
654
|
-
* @param options.
|
|
655
|
-
*
|
|
656
|
-
*
|
|
754
|
+
* @param options.assertRemovable {function}
|
|
755
|
+
* `({ published, target }) => void` -- run on the read, after the
|
|
756
|
+
* idempotent already-forgotten check and before anything is attributed or
|
|
757
|
+
* built; it throws to refuse the removal
|
|
657
758
|
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog }>}
|
|
658
759
|
*/
|
|
659
|
-
async function clientForgetEntryOnce({ store, ladderSeed, forgottenClient, knownLatentHashes = [], expectedDid, pinStore, logId,
|
|
660
|
-
//
|
|
661
|
-
//
|
|
662
|
-
|
|
760
|
+
export async function clientForgetEntryOnce({ store, projectionStore, ladderSeed, forgottenClient, knownLatentHashes = [], expectedDid, pinStore, logId, assertRemovable }) {
|
|
761
|
+
// Resolved by the skip hook on the read the entry is built on, and used by
|
|
762
|
+
// the build below -- the same snapshot, never a second read.
|
|
763
|
+
let target;
|
|
764
|
+
const entry = await ladderSignedAccountEntry({
|
|
663
765
|
store,
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
signer,
|
|
726
|
-
alsoKnownAsWeb: true,
|
|
727
|
-
...fields,
|
|
728
|
-
// The acting rung reveals itself in the entry it signs (its hash stands
|
|
729
|
-
// committed, or the rung is already revealed), and its own hash is kept
|
|
730
|
-
// committed so the carry-over convention holds for the next entry.
|
|
731
|
-
updateKeys: [...new Set([...fields.updateKeys, rung.keyMultibase])],
|
|
732
|
-
nextKeyHashes: [...new Set([...fields.nextKeyHashes, rungHash])]
|
|
766
|
+
ladderSeed,
|
|
767
|
+
expectedDid,
|
|
768
|
+
pinStore,
|
|
769
|
+
logId,
|
|
770
|
+
skip: async (published) => {
|
|
771
|
+
target = await clientRemovalTarget({
|
|
772
|
+
published,
|
|
773
|
+
client: forgottenClient
|
|
774
|
+
});
|
|
775
|
+
if (!target.present) {
|
|
776
|
+
// Already forgotten (a torn earlier run finished the entry). No
|
|
777
|
+
// projection is written on this path: this client's verification
|
|
778
|
+
// methods left the document with that entry, so its store is
|
|
779
|
+
// authorized for nothing. The next transient visit's
|
|
780
|
+
// `ensureDidWebProjection` is the mender.
|
|
781
|
+
return true;
|
|
782
|
+
}
|
|
783
|
+
await assertRemovable({ published, target });
|
|
784
|
+
return false;
|
|
785
|
+
},
|
|
786
|
+
build: async ({ published, rung }) => {
|
|
787
|
+
// The ladder vouches for its own commitments: a self-enrolled client's
|
|
788
|
+
// staged hash was committed in the same reveal entry as the next rung's
|
|
789
|
+
// hash, so without these the staged-hash attribution cannot tell the
|
|
790
|
+
// two apart. Every hash a reveal entry can have committed is for a rung
|
|
791
|
+
// at or one past the current index.
|
|
792
|
+
const ladderHashes = [];
|
|
793
|
+
for (let index = 0; index <= rung.index + 1; index++) {
|
|
794
|
+
const laddered = await ladderRung({ ladderSeed, index });
|
|
795
|
+
ladderHashes.push(await deriveNextKeyHash(laddered.keyMultibase));
|
|
796
|
+
}
|
|
797
|
+
// The removal's own filtered sets; the acting rung's key and hash are
|
|
798
|
+
// unioned back in by the shared carry-over conventions.
|
|
799
|
+
return clientRemovalFields({
|
|
800
|
+
published,
|
|
801
|
+
target: target,
|
|
802
|
+
knownLatentHashes: [...knownLatentHashes, ...ladderHashes]
|
|
803
|
+
});
|
|
804
|
+
},
|
|
805
|
+
// The post-removal projection, published while the client being removed
|
|
806
|
+
// can still write it (see the header). `webDoc` is the `alsoKnownAsWeb`
|
|
807
|
+
// projection `ladderSignedAccountEntry` always asks `updateDID` for.
|
|
808
|
+
...(projectionStore
|
|
809
|
+
? {
|
|
810
|
+
beforePublish: async ({ updated }) => {
|
|
811
|
+
if (!updated.webDoc) {
|
|
812
|
+
// `publishUpdatedLog` states the same invariant: every entry is
|
|
813
|
+
// built with `alsoKnownAsWeb`, so a missing projection is a
|
|
814
|
+
// defect. Refusing here is what keeps the removal entry from
|
|
815
|
+
// publishing with `did.json` left naming the removed client,
|
|
816
|
+
// which on a client-less account nothing could mend.
|
|
817
|
+
throw new Error('did:webvh: updateDID returned no webDoc despite the ' +
|
|
818
|
+
'did:web alsoKnownAs.');
|
|
819
|
+
}
|
|
820
|
+
await putDidWebProjection({
|
|
821
|
+
store: projectionStore,
|
|
822
|
+
webDoc: updated.webDoc
|
|
823
|
+
});
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
: {})
|
|
733
827
|
});
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
// log back straight afterwards is refused on the next read.
|
|
737
|
-
if (pinStore && logId !== undefined) {
|
|
738
|
-
await pinStore.write({ logId, pin: pinOfLog(updated.log) });
|
|
739
|
-
}
|
|
740
|
-
return { did: updated.did, doc: updated.doc, log: updated.log };
|
|
828
|
+
const settled = entry.updated ?? entry.published;
|
|
829
|
+
return { did: settled.did, doc: settled.doc, log: settled.log };
|
|
741
830
|
}
|
|
742
831
|
/**
|
|
743
832
|
* THE LADDER-VM INSTALL ENTRY (the two-entry transition ceremony's first
|
|
744
833
|
* entry): publishes the credential's ladder VM -- the stable sibling, under
|
|
745
|
-
* `assertionMethod` and `capabilityDelegation` only --
|
|
746
|
-
*
|
|
747
|
-
*
|
|
834
|
+
* `assertionMethod` and `capabilityDelegation` only -- leaving every enrolled
|
|
835
|
+
* client's inventory untouched. A ladder VM and enrolled clients are
|
|
836
|
+
* co-resident by design: the VM's life is keyed to its credential, so the
|
|
837
|
+
* document carries one per standing credential for as long as that credential
|
|
838
|
+
* stands. The entry is
|
|
748
839
|
* ladder-signed by the attributed rung, which reveals itself into
|
|
749
840
|
* `updateKeys` with its own hash kept committed (the carry-over convention),
|
|
750
841
|
* exactly the removal entry's rung math -- so a torn ceremony's re-run
|
|
@@ -790,72 +881,207 @@ export async function installLadderVmWebvh(options) {
|
|
|
790
881
|
* @param options {object} see {@link installLadderVmWebvh}
|
|
791
882
|
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog, installed: boolean }>}
|
|
792
883
|
*/
|
|
793
|
-
async function installLadderVmWebvhOnce(
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
store,
|
|
798
|
-
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
799
|
-
...(pinStore ? { pinStore } : {}),
|
|
800
|
-
...(logId !== undefined ? { logId } : {})
|
|
884
|
+
async function installLadderVmWebvhOnce(options) {
|
|
885
|
+
const { changed, ...settled } = await setLadderVmPresenceOnce({
|
|
886
|
+
...options,
|
|
887
|
+
present: true
|
|
801
888
|
});
|
|
802
|
-
|
|
889
|
+
return { ...settled, installed: changed };
|
|
890
|
+
}
|
|
891
|
+
/**
|
|
892
|
+
* ONE LADDER-VM PRESENCE ENTRY, in either direction: `present: true`
|
|
893
|
+
* publishes this credential's ladder VM, `present: false` strikes it, and the
|
|
894
|
+
* entries are otherwise the same edit read backwards -- the VM in or out of
|
|
895
|
+
* `verificationMethod`, `assertionMethod`, and `capabilityDelegation`, with
|
|
896
|
+
* `authentication`, `keyAgreement`, and `capabilityInvocation` re-stated
|
|
897
|
+
* untouched. That relation asymmetry (`assertionMethod` and
|
|
898
|
+
* `capabilityDelegation` only, never `authentication` or
|
|
899
|
+
* `capabilityInvocation`) is what the recognition reads and what keeps a
|
|
900
|
+
* ladder VM out of every client listing, so the two directions must agree on
|
|
901
|
+
* it exactly; stating it once is the point of the merge.
|
|
902
|
+
*
|
|
903
|
+
* The id derives from the ladder seed, so the entry reaches ONE credential's
|
|
904
|
+
* VM: another standing credential's ladder VM, and every enrolled client's
|
|
905
|
+
* inventory, stand untouched. Idempotent in both directions -- a document
|
|
906
|
+
* already in the asked-for state is a no-op returning `changed: false`,
|
|
907
|
+
* detected BEFORE the rung attribution so a re-run over a retired credential
|
|
908
|
+
* returns unchanged rather than failing closed.
|
|
909
|
+
*
|
|
910
|
+
* @param options {object} see {@link installLadderVmWebvh}, plus:
|
|
911
|
+
* @param options.present {boolean} the state the entry leaves the ladder VM
|
|
912
|
+
* in
|
|
913
|
+
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog, changed: boolean }>}
|
|
914
|
+
*/
|
|
915
|
+
async function setLadderVmPresenceOnce({ store, ladderSeed, expectedDid, pinStore, logId, present }) {
|
|
803
916
|
const ladderVmKey = await ladderVmKeyMultibase({ ladderSeed });
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
verificationMethods,
|
|
842
|
-
authentication: relationIds(doc.authentication),
|
|
843
|
-
assertionMethod: withVm(doc.assertionMethod),
|
|
844
|
-
keyAgreement: relationIds(doc.keyAgreement),
|
|
845
|
-
capabilityInvocation: relationIds(doc.capabilityInvocation),
|
|
846
|
-
capabilityDelegation: withVm(doc.capabilityDelegation)
|
|
917
|
+
const entry = await ladderSignedAccountEntry({
|
|
918
|
+
store,
|
|
919
|
+
ladderSeed,
|
|
920
|
+
expectedDid,
|
|
921
|
+
pinStore,
|
|
922
|
+
logId,
|
|
923
|
+
// Already in the asked-for state: a torn earlier run published the entry,
|
|
924
|
+
// the account is mid-transition, or the credential never bound a VM here.
|
|
925
|
+
skip: published => ladderVmIds({ doc: published.doc }).includes(`${published.did}#${ladderVmKey}`) === present,
|
|
926
|
+
build: ({ published }) => {
|
|
927
|
+
const { did, doc } = published;
|
|
928
|
+
const ladderVmId = `${did}#${ladderVmKey}`;
|
|
929
|
+
const withoutVm = (relation) => relationIds(relation).filter(id => id !== ladderVmId);
|
|
930
|
+
const withVm = (relation) => [...new Set([...relationIds(relation), ladderVmId])];
|
|
931
|
+
const inRelation = present ? withVm : withoutVm;
|
|
932
|
+
const otherMethods = (doc.verificationMethod ?? []).filter(method => method.id !== ladderVmId);
|
|
933
|
+
return {
|
|
934
|
+
verificationMethods: present
|
|
935
|
+
? [
|
|
936
|
+
...otherMethods,
|
|
937
|
+
ladderVerificationMethod({
|
|
938
|
+
controller: did,
|
|
939
|
+
publicKeyMultibase: ladderVmKey
|
|
940
|
+
})
|
|
941
|
+
]
|
|
942
|
+
: otherMethods,
|
|
943
|
+
// The ladder VM's relation asymmetry: `assertionMethod` and
|
|
944
|
+
// `capabilityDelegation` only -- no `authentication`, no
|
|
945
|
+
// `capabilityInvocation` -- which is also what keeps it out of every
|
|
946
|
+
// client listing.
|
|
947
|
+
authentication: relationIds(doc.authentication),
|
|
948
|
+
assertionMethod: inRelation(doc.assertionMethod),
|
|
949
|
+
keyAgreement: relationIds(doc.keyAgreement),
|
|
950
|
+
capabilityInvocation: relationIds(doc.capabilityInvocation),
|
|
951
|
+
capabilityDelegation: inRelation(doc.capabilityDelegation)
|
|
952
|
+
};
|
|
953
|
+
}
|
|
847
954
|
});
|
|
848
|
-
|
|
849
|
-
// Advance the pin to what this entry just published, so a host rolling the
|
|
850
|
-
// log back straight afterwards is refused on the next read.
|
|
851
|
-
if (pinStore && logId !== undefined) {
|
|
852
|
-
await pinStore.write({ logId, pin: pinOfLog(updated.log) });
|
|
853
|
-
}
|
|
955
|
+
const settled = entry.updated ?? entry.published;
|
|
854
956
|
return {
|
|
855
|
-
did:
|
|
856
|
-
doc:
|
|
857
|
-
log:
|
|
858
|
-
|
|
957
|
+
did: settled.did,
|
|
958
|
+
doc: settled.doc,
|
|
959
|
+
log: settled.log,
|
|
960
|
+
changed: entry.updated !== undefined
|
|
859
961
|
};
|
|
860
962
|
}
|
|
963
|
+
/**
|
|
964
|
+
* THE LADDER-VM STRIKE ENTRY: takes THIS credential's ladder VM out of the
|
|
965
|
+
* document -- from `verificationMethod`, `assertionMethod`, and
|
|
966
|
+
* `capabilityDelegation` -- and touches nothing else. The id comes from the
|
|
967
|
+
* ladder seed, so the strike reaches one credential's VM: another standing
|
|
968
|
+
* credential's ladder VM, and every enrolled client's inventory, stand
|
|
969
|
+
* untouched. The account-wide filter the transient recovery's add-and-retire
|
|
970
|
+
* entry runs is a different rule and stays there.
|
|
971
|
+
*
|
|
972
|
+
* Paired with {@link installLadderVmWebvh} it is the last-client
|
|
973
|
+
* transition's opening move: the strike, then the reinstall of the identical
|
|
974
|
+
* VM. That pair is what supplies the transition with an
|
|
975
|
+
* inventory-changing document version for its one ladder-signed roster
|
|
976
|
+
* append, on an account whose VM already stands (the credential-keyed
|
|
977
|
+
* lifecycle installs it at bind time). The republished node is the same
|
|
978
|
+
* `<accountDid>#<multibase>`, and a zcap delegation proof carries no version
|
|
979
|
+
* anchor, so any unexpired ladder-signed delegation resumes verifying.
|
|
980
|
+
*
|
|
981
|
+
* Ladder-signed by the attributed rung, which reveals itself into
|
|
982
|
+
* `updateKeys` with its own hash kept committed (the carry-over convention)
|
|
983
|
+
* -- the install and removal entries' rung math exactly.
|
|
984
|
+
*
|
|
985
|
+
* Idempotent: a document carrying no ladder VM of this credential's returns
|
|
986
|
+
* unchanged with `struck: false`. The entry publishes conditionally on the
|
|
987
|
+
* log this call read; a lost race re-runs, re-attributes, and rebases on the
|
|
988
|
+
* winner's head.
|
|
989
|
+
*
|
|
990
|
+
* @param options {object}
|
|
991
|
+
* @param options.store {UnlockLogStore} the credential's delegated
|
|
992
|
+
* `did.jsonl` bridge store
|
|
993
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
994
|
+
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
995
|
+
* to, from the caller's stored account pointer
|
|
996
|
+
* @param [options.pinStore] {ResourceLogPinStore} the caller's chain-head
|
|
997
|
+
* pins: the read inside each attempt is checked against the pinned head,
|
|
998
|
+
* and the pin advances to the head this entry publishes
|
|
999
|
+
* @param [options.logId] {string} the account log's pin slot
|
|
1000
|
+
* (`accountLogPinId({ spaceId })`); required whenever a `pinStore` is
|
|
1001
|
+
* supplied
|
|
1002
|
+
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog, struck: boolean }>}
|
|
1003
|
+
* the account DID and the document and log as the strike entry leaves them
|
|
1004
|
+
* (unchanged on the idempotent no-op path); `struck` says whether the entry
|
|
1005
|
+
* ran on this call
|
|
1006
|
+
*/
|
|
1007
|
+
export async function strikeLadderVmWebvh(options) {
|
|
1008
|
+
return withLogConflictRetry(() => strikeLadderVmWebvhOnce(options));
|
|
1009
|
+
}
|
|
1010
|
+
/**
|
|
1011
|
+
* One attempt of {@link strikeLadderVmWebvh}, re-invoked by the conflict
|
|
1012
|
+
* retry.
|
|
1013
|
+
*
|
|
1014
|
+
* @param options {object} see {@link strikeLadderVmWebvh}
|
|
1015
|
+
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog, struck: boolean }>}
|
|
1016
|
+
*/
|
|
1017
|
+
async function strikeLadderVmWebvhOnce(options) {
|
|
1018
|
+
const { changed, ...settled } = await setLadderVmPresenceOnce({
|
|
1019
|
+
...options,
|
|
1020
|
+
present: false
|
|
1021
|
+
});
|
|
1022
|
+
return { ...settled, struck: changed };
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* THE STANDALONE REVEAL-AND-COMMIT ENTRY: reveals this credential's currently
|
|
1026
|
+
* committed ladder rung into `updateKeys`, keeping its own hash committed and
|
|
1027
|
+
* committing the next rung's (the carry-over convention). It is the same
|
|
1028
|
+
* entry {@link selfEnrollWebvhClient} writes first, minus the enrolling
|
|
1029
|
+
* client's hashes, and it exists so a credential-only visit can sign an
|
|
1030
|
+
* account-log entry of its own -- the `#DelegatedClients` pointer move -- on
|
|
1031
|
+
* an account whose self-enrollment already spent the previously revealed
|
|
1032
|
+
* rung.
|
|
1033
|
+
*
|
|
1034
|
+
* A rung already revealed (a torn earlier run, or a racing ceremony that got
|
|
1035
|
+
* there first) is a no-op: nothing is published and `revealed: false` comes
|
|
1036
|
+
* back. A ladder the log commits no rung of at all, and an ambiguous
|
|
1037
|
+
* attribution, fail closed with `LadderAttributionError`.
|
|
1038
|
+
*
|
|
1039
|
+
* This entry retires nothing. The revealed rung stands in `updateKeys` until
|
|
1040
|
+
* a later self-enrollment's add entry spends it or the credential retires;
|
|
1041
|
+
* the caller's comment states the consequence at its own site.
|
|
1042
|
+
*
|
|
1043
|
+
* No conflict retry of its own: a caller pairing this entry with a second one
|
|
1044
|
+
* must run both inside ONE {@link withLogConflictRetry}, so a race lost
|
|
1045
|
+
* between them re-runs the attribution rather than signing with a rung the
|
|
1046
|
+
* winner consumed.
|
|
1047
|
+
*
|
|
1048
|
+
* @param options {object}
|
|
1049
|
+
* @param options.store {UnlockLogStore} the credential's delegated
|
|
1050
|
+
* `did.jsonl` bridge store
|
|
1051
|
+
* @param options.ladderSeed {Uint8Array} the credential's ladder seed
|
|
1052
|
+
* @param [options.expectedDid] {string} the account DID the log must
|
|
1053
|
+
* resolve to, from the caller's stored account pointer
|
|
1054
|
+
* @param [options.pinStore] {ResourceLogPinStore} the caller's chain-head
|
|
1055
|
+
* pins: the read is checked against the pinned head, and the pin advances
|
|
1056
|
+
* to the head this entry publishes
|
|
1057
|
+
* @param [options.logId] {string} the account log's pin slot
|
|
1058
|
+
* (`accountLogPinId({ spaceId })`); required whenever a `pinStore` is
|
|
1059
|
+
* supplied
|
|
1060
|
+
* @returns {Promise<{ revealed: boolean }>} whether this call published the
|
|
1061
|
+
* entry
|
|
1062
|
+
*/
|
|
1063
|
+
export async function revealLadderRungWebvh({ store, ladderSeed, expectedDid, pinStore, logId }) {
|
|
1064
|
+
const entry = await ladderSignedAccountEntry({
|
|
1065
|
+
store,
|
|
1066
|
+
ladderSeed,
|
|
1067
|
+
expectedDid,
|
|
1068
|
+
pinStore,
|
|
1069
|
+
logId,
|
|
1070
|
+
build: async ({ rung, state }) => {
|
|
1071
|
+
// A rung already revealed (a torn earlier run, or a racing ceremony
|
|
1072
|
+
// that got there first) leaves nothing to publish. The decline sits
|
|
1073
|
+
// after the attribution because the attribution is what answers it.
|
|
1074
|
+
if (state === 'revealed') {
|
|
1075
|
+
return undefined;
|
|
1076
|
+
}
|
|
1077
|
+
// The entry commits the next rung only; the acting rung's own key and
|
|
1078
|
+
// carry-over hash come from the shared reveal and carry-over unions.
|
|
1079
|
+
const nextRung = await ladderRung({ ladderSeed, index: rung.index + 1 });
|
|
1080
|
+
return {
|
|
1081
|
+
commitHashes: [await deriveNextKeyHash(nextRung.keyMultibase)]
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
return { revealed: entry.updated !== undefined };
|
|
1086
|
+
}
|
|
861
1087
|
//# sourceMappingURL=ladderAnchored.js.map
|