@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
|
@@ -12,11 +12,15 @@
|
|
|
12
12
|
* key (a passphrase), so the world-readable document carries a check on the
|
|
13
13
|
* key without carrying the key -- and `nextKeyHashes` gains the hash of the
|
|
14
14
|
* credential's current update key (a ladder rung, or a code's single derived
|
|
15
|
-
* key).
|
|
15
|
+
* key). A credential that carries a ladder gains its LADDER VM in the same
|
|
16
|
+
* entry, under `assertionMethod` and `capabilityDelegation`: the VM's life is
|
|
17
|
+
* the credential's, installed when it becomes standing and struck when it
|
|
18
|
+
* retires, and enrollment never touches it.
|
|
16
19
|
* Decryption standing, authority latent: the credential's update key joins
|
|
17
|
-
* `updateKeys` nowhere, and
|
|
18
|
-
* listings (keyed on `capabilityInvocation`) and revocation
|
|
19
|
-
* see
|
|
20
|
+
* `updateKeys` nowhere, and the key-agreement entry is deliberately unmarked,
|
|
21
|
+
* so client listings (keyed on `capabilityInvocation`) and revocation
|
|
22
|
+
* removals never see it; the VM's relation asymmetry keeps it out of the same
|
|
23
|
+
* listings. {@link publishUnlockKey} / {@link removeUnlockKey} are one merged
|
|
20
24
|
* add/remove pair, shared verbatim by the recovery-code wrappers.
|
|
21
25
|
*
|
|
22
26
|
* The ceremonies that EXERCISE a credential's ladder against the account log
|
|
@@ -24,16 +28,90 @@
|
|
|
24
28
|
* one-entry forget -- live in `clientAnnex/ladderAnchored.ts`. What stays
|
|
25
29
|
* here is the verify-side half every wallet needs regardless of account configuration.
|
|
26
30
|
*/
|
|
27
|
-
import { deriveNextKeyHash
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
31
|
+
import { deriveNextKeyHash } from '@interop/did-method-webvh';
|
|
32
|
+
import { MULTIKEY_COMMITMENT_VM_TYPE, MULTIKEY_VM_TYPE, ladderVerificationMethod, readPublishedLogOrThrow, withLogConflictRetry } from '../webvh/didWebvh.js';
|
|
33
|
+
import { signAccountEntry } from '../webvh/accountEntry.js';
|
|
34
|
+
import { preEntryProjectionPublisher } from '../webvh/didWebProjection.js';
|
|
35
|
+
import { ladderVmIds, relationIds } from '../resourceLog/document.js';
|
|
30
36
|
// The one deliberate base-side dependency on the annex subpath, pinned as an
|
|
31
|
-
// exception in the lint rule:
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
// exception in the lint rule: this module resolves a credential's CURRENT
|
|
38
|
+
// ladder inventory from the log itself (the shared attribution helpers in
|
|
39
|
+
// `clientAnnex/ladder.ts`), never touching the annex log machinery, and
|
|
40
|
+
// derives the credential's ladder VM from its seed at the install.
|
|
41
|
+
import { attributeLadderInventory, LadderAttributionError, ladderVmIdsIntroducedWithCredential, ladderVmKeyMultibase } from '../clientAnnex/ladder.js';
|
|
42
|
+
/**
|
|
43
|
+
* The ladder inventory the removal edit resolved from the log diverges from
|
|
44
|
+
* what the caller was told one read earlier. A ceremony that names its
|
|
45
|
+
* expected ladder VM ids (the retirement's stage 0, which hands the same list
|
|
46
|
+
* to the dependent-record re-mint pass) gets this refusal BEFORE the edit is
|
|
47
|
+
* published, so a concurrent ceremony -- or a host serving different log
|
|
48
|
+
* versions to the two reads -- cannot make the strike diverge from what the
|
|
49
|
+
* re-mint pass acted on. Nothing is written. Matched on `name` (the error
|
|
50
|
+
* crosses app-injected seams that may resolve to another copy of this
|
|
51
|
+
* package).
|
|
52
|
+
*/
|
|
53
|
+
export class LadderInventoryDriftError extends Error {
|
|
54
|
+
expected;
|
|
55
|
+
attributed;
|
|
56
|
+
constructor({ expected, attributed }) {
|
|
57
|
+
super('did:webvh: the published log attributes a different ladder VM set to ' +
|
|
58
|
+
'this credential than the caller resolved a moment earlier ' +
|
|
59
|
+
`(expected ${expected.join(', ') || '(none)'}; attributed ` +
|
|
60
|
+
`${attributed.join(', ') || '(none)'}); the inventory edit was not ` +
|
|
61
|
+
'published. Re-run the retirement on a fresh read.');
|
|
62
|
+
this.name = 'LadderInventoryDriftError';
|
|
63
|
+
this.expected = expected;
|
|
64
|
+
this.attributed = attributed;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A retirement whose ladder attribution could not claim the retired
|
|
69
|
+
* credential's ladder VM, refused with nothing written. The shape is the
|
|
70
|
+
* seedless strike claiming nothing: the credential still stands in the
|
|
71
|
+
* document, the walk struck no ladder VM, and ladder VMs stand there that it
|
|
72
|
+
* could not claim. A leftover VM would keep the retired credential's
|
|
73
|
+
* delegation authority alive -- under `capabilityDelegation` it can still
|
|
74
|
+
* sign a DELETE-only capability on the account Space -- and nothing
|
|
75
|
+
* downstream can tell such a leftover from a sibling credential's standing
|
|
76
|
+
* VM, so the retirement is the one place the state can be closed
|
|
77
|
+
* (`decisions/0015`).
|
|
78
|
+
*
|
|
79
|
+
* `unclaimedLadderVmIds` names every ladder VM the walk left unclaimed, and
|
|
80
|
+
* `anchorKeyMultibase` the update key the walk was anchored on (a recovery
|
|
81
|
+
* code's revocation anchors on the rung-0 multibase the registry recorded at
|
|
82
|
+
* issuance). On a
|
|
83
|
+
* multi-credential account that list carries the siblings' VMs beside the
|
|
84
|
+
* retired credential's, since telling them apart is exactly what the walk
|
|
85
|
+
* could not do. `retryableWithLadderSeed` says whether a retry supplying the
|
|
86
|
+
* credential's ladder seed can let attribution succeed. The gate raises this
|
|
87
|
+
* error only from a seedless claim (a seeded one either strikes the derived
|
|
88
|
+
* VM or proves there is none), so the hint is `true` from this library; the
|
|
89
|
+
* member is the wallets' read for the retry they offer.
|
|
90
|
+
* Matched on `name` (the error crosses app-injected seams that may resolve
|
|
91
|
+
* to another copy of this package).
|
|
92
|
+
*/
|
|
93
|
+
export class UnclaimedLadderVmRetirementError extends Error {
|
|
94
|
+
unclaimedLadderVmIds;
|
|
95
|
+
retryableWithLadderSeed;
|
|
96
|
+
anchorKeyMultibase;
|
|
97
|
+
constructor({ unclaimedLadderVmIds, retryableWithLadderSeed, anchorKeyMultibase }) {
|
|
98
|
+
super("did:webvh: the retirement cannot claim the retired credential's ladder " +
|
|
99
|
+
`VM (standing unclaimed: ${unclaimedLadderVmIds.join(', ')}` +
|
|
100
|
+
(anchorKeyMultibase === undefined
|
|
101
|
+
? ''
|
|
102
|
+
: `; anchored on ${anchorKeyMultibase}`) +
|
|
103
|
+
'); nothing was published and the credential still stands. ' +
|
|
104
|
+
(retryableWithLadderSeed
|
|
105
|
+
? "Retry with the credential's ladder seed in hand."
|
|
106
|
+
: 'No retry with the ladder seed can claim it.'));
|
|
107
|
+
this.name = 'UnclaimedLadderVmRetirementError';
|
|
108
|
+
this.unclaimedLadderVmIds = unclaimedLadderVmIds;
|
|
109
|
+
this.retryableWithLadderSeed = retryableWithLadderSeed;
|
|
110
|
+
if (anchorKeyMultibase !== undefined) {
|
|
111
|
+
this.anchorKeyMultibase = anchorKeyMultibase;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
37
115
|
/**
|
|
38
116
|
* The verification-method id a credential's key-agreement entry publishes
|
|
39
117
|
* under: `<did>#<multibase>` for a verbatim key (indistinguishable by id from
|
|
@@ -51,6 +129,223 @@ export function unlockKeyVmId({ did, keyAgreement }) {
|
|
|
51
129
|
: keyAgreement.commitment;
|
|
52
130
|
return `${did}#${fragment}`;
|
|
53
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* What of a standing credential's ladder currently stands in the published
|
|
134
|
+
* log, resolved with the recorded update key as ANCHOR and the credential's
|
|
135
|
+
* own key-agreement id as the attribution's second arm. The removal edit uses
|
|
136
|
+
* it to know what to strike; the retirement ceremony uses it one stage
|
|
137
|
+
* earlier, to name the ladder VM it is about to strike to the pass that
|
|
138
|
+
* re-mints whatever that VM signed for other credentials.
|
|
139
|
+
*
|
|
140
|
+
* It lives here rather than in the ceremony because this module is the one
|
|
141
|
+
* base-side holder of the annex attribution helpers (the pinned lint
|
|
142
|
+
* exception).
|
|
143
|
+
*
|
|
144
|
+
* @param options {object}
|
|
145
|
+
* @param options.log {DIDLog} a resolved, caller-verified log
|
|
146
|
+
* @param options.did {string} the account DID the log resolves to
|
|
147
|
+
* @param options.unlockKeys {StandingUnlockKeys} the credential's recorded
|
|
148
|
+
* public inventory
|
|
149
|
+
* @param [options.ladderSeed] {Uint8Array} the credential's ladder seed,
|
|
150
|
+
* when the ceremony holds it
|
|
151
|
+
* @returns {Promise<LadderStandingInventory>}
|
|
152
|
+
*/
|
|
153
|
+
export async function attributeUnlockLadderInventory({ log, did, unlockKeys, ladderSeed }) {
|
|
154
|
+
return attributeLadderInventory({
|
|
155
|
+
log,
|
|
156
|
+
anchorKeyMultibase: unlockKeys.updateKeyMultibase,
|
|
157
|
+
credentialVmId: unlockKeyVmId({
|
|
158
|
+
did,
|
|
159
|
+
keyAgreement: unlockKeys.keyAgreement
|
|
160
|
+
}),
|
|
161
|
+
...(ladderSeed ? { ladderSeed } : {})
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* What of the document's ladder VMs a credential's attribution claims: the
|
|
166
|
+
* VM its seed derives (when the ceremony holds one), plus every VM the log
|
|
167
|
+
* attributes to its ladder. Resolved once here and shared by the removal
|
|
168
|
+
* edit, the retirement ceremony's pre-edit stage, and the read-only
|
|
169
|
+
* pre-flight, so the three agree on what is struck and what is left.
|
|
170
|
+
*
|
|
171
|
+
* `struck` is what the removal edit strikes: the derived id when it stands,
|
|
172
|
+
* and the attributed ids. `unclaimed` is every ladder VM standing in the
|
|
173
|
+
* document that neither the seed nor the attribution claims -- on a
|
|
174
|
+
* multi-credential account, the siblings' VMs at least. A supplied seed also
|
|
175
|
+
* cross-checks the attribution: a log attributing a VM the seed does not
|
|
176
|
+
* derive refuses with {@link LadderAttributionError}.
|
|
177
|
+
*
|
|
178
|
+
* @param options {object}
|
|
179
|
+
* @param options.doc {DIDDoc} the document the attribution ran over
|
|
180
|
+
* @param options.did {string} the account DID
|
|
181
|
+
* @param options.inventory {LadderStandingInventory} the credential's
|
|
182
|
+
* attributed ladder inventory ({@link attributeUnlockLadderInventory})
|
|
183
|
+
* @param [options.ladderSeed] {Uint8Array} the credential's ladder seed
|
|
184
|
+
* @returns {Promise<{ ladderVmId?: string, struck: string[], unclaimed:
|
|
185
|
+
* string[] }>} the seed-derived VM id when a seed was held
|
|
186
|
+
*/
|
|
187
|
+
export async function ladderVmClaimOf({ doc, did, inventory, ladderSeed }) {
|
|
188
|
+
const ladderVmId = ladderSeed
|
|
189
|
+
? `${did}#${await ladderVmKeyMultibase({ ladderSeed })}`
|
|
190
|
+
: undefined;
|
|
191
|
+
if (ladderVmId !== undefined) {
|
|
192
|
+
const foreign = inventory.ladderVmIds.filter(id => id !== ladderVmId);
|
|
193
|
+
if (foreign.length > 0) {
|
|
194
|
+
throw new LadderAttributionError("The published log attributes a ladder VM this credential's seed " +
|
|
195
|
+
'does not derive; refusing to strike on an ambiguous ' +
|
|
196
|
+
'attribution.');
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
const standing = ladderVmIds({ doc });
|
|
200
|
+
const struck = new Set();
|
|
201
|
+
if (ladderVmId !== undefined && standing.includes(ladderVmId)) {
|
|
202
|
+
struck.add(ladderVmId);
|
|
203
|
+
}
|
|
204
|
+
for (const id of inventory.ladderVmIds) {
|
|
205
|
+
struck.add(id);
|
|
206
|
+
}
|
|
207
|
+
const claimed = new Set([
|
|
208
|
+
...struck,
|
|
209
|
+
...(ladderVmId !== undefined ? [ladderVmId] : [])
|
|
210
|
+
]);
|
|
211
|
+
return {
|
|
212
|
+
...(ladderVmId !== undefined ? { ladderVmId } : {}),
|
|
213
|
+
struck: [...struck],
|
|
214
|
+
unclaimed: standing.filter(id => !claimed.has(id))
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* The retirement gate (`decisions/0015`): refuses, with
|
|
219
|
+
* {@link UnclaimedLadderVmRetirementError}, a retirement of a
|
|
220
|
+
* ladder-carrying credential whose SEEDLESS claim struck nothing while a
|
|
221
|
+
* ladder VM that COULD BE THIS CREDENTIAL'S stands unclaimed and the
|
|
222
|
+
* credential itself still stands in the document. Deliberately narrower than
|
|
223
|
+
* "`unclaimed` is non-empty", which every retirement on a healthy
|
|
224
|
+
* multi-credential account produces, and narrower again than the standing
|
|
225
|
+
* unclaimed set: a sibling credential's VM is nothing this retirement could
|
|
226
|
+
* leave behind.
|
|
227
|
+
*
|
|
228
|
+
* Which unclaimed VMs are candidates is
|
|
229
|
+
* {@link ladderVmIdsIntroducedWithCredential}'s question, read off the log's
|
|
230
|
+
* entry shapes rather than off an attribution that has already refused: a VM
|
|
231
|
+
* introduced by the entry that introduced this credential's `keyAgreement`
|
|
232
|
+
* member, or by the entry that committed or authorized its anchor (the split
|
|
233
|
+
* bind, whose key and authority entries sit two versions apart). None, and
|
|
234
|
+
* the credential never had a VM to leave standing, so the retirement
|
|
235
|
+
* completes and strikes what it can. That is the torn issuance's orphan -- a
|
|
236
|
+
* credential with a `keyAgreement` member, no ladder VM, and no committed
|
|
237
|
+
* rung -- which previously could never be removed at all, since a sibling's
|
|
238
|
+
* standing VM refused it forever.
|
|
239
|
+
*
|
|
240
|
+
* Two shapes pass ahead of that. A credential whose `keyAgreement` member is
|
|
241
|
+
* already gone is a completed retirement re-running. And a claim resolved
|
|
242
|
+
* WITH the seed never refuses: the derived VM is either standing (and
|
|
243
|
+
* struck) or absent, and an absent derived VM is proof the credential has
|
|
244
|
+
* nothing to claim -- the last-client transition torn between its strike and
|
|
245
|
+
* reinstall entries leaves exactly that, and a seeded retirement there must
|
|
246
|
+
* complete rather than wait on the transition's re-run. So the error's retry
|
|
247
|
+
* hint is `true` whenever this gate raises it.
|
|
248
|
+
*
|
|
249
|
+
* The caller decides whether the credential carries a ladder.
|
|
250
|
+
*
|
|
251
|
+
* @param options {object}
|
|
252
|
+
* @param options.log {DIDLog} the verified log the claim was resolved over
|
|
253
|
+
* @param options.doc {DIDDoc} the document the claim was resolved over
|
|
254
|
+
* @param options.credentialVmId {string} the credential's `keyAgreement`
|
|
255
|
+
* verification-method id ({@link unlockKeyVmId})
|
|
256
|
+
* @param options.claim {{ ladderVmId?: string, struck: string[], unclaimed:
|
|
257
|
+
* string[] }} from {@link ladderVmClaimOf}
|
|
258
|
+
* @param [options.anchorKeyMultibase] {string} the update key the walk was
|
|
259
|
+
* anchored on: the candidate reading's second anchor form, and named in
|
|
260
|
+
* the refusal
|
|
261
|
+
* @returns {Promise<void>}
|
|
262
|
+
*/
|
|
263
|
+
export async function assertLadderVmClaimed({ log, doc, credentialVmId, claim, anchorKeyMultibase }) {
|
|
264
|
+
const credentialStands = (doc.verificationMethod ?? []).some(method => method.id === credentialVmId);
|
|
265
|
+
if (!credentialStands ||
|
|
266
|
+
claim.ladderVmId !== undefined ||
|
|
267
|
+
claim.struck.length > 0 ||
|
|
268
|
+
claim.unclaimed.length === 0) {
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const candidates = await ladderVmIdsIntroducedWithCredential({
|
|
272
|
+
log,
|
|
273
|
+
credentialVmId,
|
|
274
|
+
...(anchorKeyMultibase !== undefined ? { anchorKeyMultibase } : {})
|
|
275
|
+
});
|
|
276
|
+
const unclaimed = claim.unclaimed.filter(id => candidates.includes(id));
|
|
277
|
+
if (unclaimed.length === 0) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
throw new UnclaimedLadderVmRetirementError({
|
|
281
|
+
unclaimedLadderVmIds: unclaimed,
|
|
282
|
+
retryableWithLadderSeed: true,
|
|
283
|
+
...(anchorKeyMultibase !== undefined ? { anchorKeyMultibase } : {})
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* The retirement gate run read-only, before anything is written: one pinned
|
|
288
|
+
* read of the account log, the credential's ladder attribution, and
|
|
289
|
+
* {@link assertLadderVmClaimed} over the result. A caller that establishes a
|
|
290
|
+
* replacement credential before it retires the old one (a passphrase change,
|
|
291
|
+
* a tap-confirmed passkey removal) runs this first, so a gate refusal lands
|
|
292
|
+
* the way an invalid-input check does -- nothing established, no
|
|
293
|
+
* pending-shaped registry entry written -- rather than after establishment,
|
|
294
|
+
* where the refusal would leave a torn state no seedless repair can clear.
|
|
295
|
+
* The in-ceremony gate stays as defense in depth, and it is what answers a
|
|
296
|
+
* log entry landing between the pre-flight and the retirement: the
|
|
297
|
+
* pre-flight's verdict holds for the head it read, and nothing binds the two
|
|
298
|
+
* reads.
|
|
299
|
+
*
|
|
300
|
+
* @param options {object}
|
|
301
|
+
* @param options.idStore {UnlockLogStore} the account's `id` collection
|
|
302
|
+
* read side
|
|
303
|
+
* @param options.unlockKeys {StandingUnlockKeys} the credential's recorded
|
|
304
|
+
* public inventory
|
|
305
|
+
* @param [options.ladderSeed] {Uint8Array} the credential's ladder seed,
|
|
306
|
+
* when the caller holds it
|
|
307
|
+
* @param [options.expectedDid] {string} the account DID the log must
|
|
308
|
+
* resolve to
|
|
309
|
+
* @param [options.pinStore] {ResourceLogPinStore} the caller's chain-head
|
|
310
|
+
* pins
|
|
311
|
+
* @param [options.logId] {string} the account log's pin slot; required
|
|
312
|
+
* whenever a `pinStore` is supplied
|
|
313
|
+
* @returns {Promise<LadderVmRemovalReport>} what the retirement would
|
|
314
|
+
* strike and what it would leave unclaimed
|
|
315
|
+
*/
|
|
316
|
+
export async function preflightUnlockCredentialRetirement({ idStore, unlockKeys, ladderSeed, expectedDid, pinStore, logId }) {
|
|
317
|
+
const published = await readPublishedLogOrThrow({
|
|
318
|
+
idStore,
|
|
319
|
+
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
320
|
+
...(pinStore ? { pinStore } : {}),
|
|
321
|
+
...(logId !== undefined ? { logId } : {}),
|
|
322
|
+
missingMessage: 'did:webvh: did.jsonl is missing; nothing to retire from.'
|
|
323
|
+
});
|
|
324
|
+
const { did, doc } = published;
|
|
325
|
+
const inventory = await attributeUnlockLadderInventory({
|
|
326
|
+
log: published.log,
|
|
327
|
+
did,
|
|
328
|
+
unlockKeys,
|
|
329
|
+
...(ladderSeed ? { ladderSeed } : {})
|
|
330
|
+
});
|
|
331
|
+
const claim = await ladderVmClaimOf({
|
|
332
|
+
doc,
|
|
333
|
+
did,
|
|
334
|
+
inventory,
|
|
335
|
+
...(ladderSeed ? { ladderSeed } : {})
|
|
336
|
+
});
|
|
337
|
+
await assertLadderVmClaimed({
|
|
338
|
+
log: published.log,
|
|
339
|
+
doc,
|
|
340
|
+
credentialVmId: unlockKeyVmId({
|
|
341
|
+
did,
|
|
342
|
+
keyAgreement: unlockKeys.keyAgreement
|
|
343
|
+
}),
|
|
344
|
+
claim,
|
|
345
|
+
anchorKeyMultibase: unlockKeys.updateKeyMultibase
|
|
346
|
+
});
|
|
347
|
+
return { struck: claim.struck, unclaimed: claim.unclaimed };
|
|
348
|
+
}
|
|
54
349
|
/**
|
|
55
350
|
* The credential's `keyAgreement` verification method: an ordinary unmarked
|
|
56
351
|
* entry carrying either the key verbatim (a `Multikey` with
|
|
@@ -84,50 +379,46 @@ export function unlockKeyVerificationMethod({ did, keyAgreement }) {
|
|
|
84
379
|
publicKeyCommitment: keyAgreement.commitment
|
|
85
380
|
};
|
|
86
381
|
}
|
|
87
|
-
/**
|
|
88
|
-
* Reads and resolves the published log through the narrow store seam.
|
|
89
|
-
*
|
|
90
|
-
* @param options {object}
|
|
91
|
-
* @param options.store {UnlockLogStore}
|
|
92
|
-
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
93
|
-
* to, where the caller holds one
|
|
94
|
-
* @param [options.pinStore] {ResourceLogPinStore} the caller's chain-head
|
|
95
|
-
* pins; a served log that is a rollback, a fork, or an identity switch
|
|
96
|
-
* against the pinned head is refused (`ResourceLogContinuityError`)
|
|
97
|
-
* @param [options.logId] {string} the account log's pin slot
|
|
98
|
-
* (`accountLogPinId({ spaceId })`); required whenever a `pinStore` is
|
|
99
|
-
* supplied
|
|
100
|
-
* @returns {Promise<PublishedWebvhLog>}
|
|
101
|
-
*/
|
|
102
|
-
export async function readLogOrThrow({ store, expectedDid, pinStore, logId }) {
|
|
103
|
-
// readPublishedLog only calls getIdResourceRaw, so the narrow seam is safe.
|
|
104
|
-
const published = await readPublishedLog({
|
|
105
|
-
idStore: store,
|
|
106
|
-
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
107
|
-
...(pinStore ? { pinStore } : {}),
|
|
108
|
-
...(logId !== undefined ? { logId } : {})
|
|
109
|
-
});
|
|
110
|
-
if (!published) {
|
|
111
|
-
throw new Error('did:webvh: did.jsonl is missing; nothing to enroll into.');
|
|
112
|
-
}
|
|
113
|
-
return published;
|
|
114
|
-
}
|
|
115
382
|
/**
|
|
116
383
|
* BIND (run by an enrolled client, root authority): publishes a standing
|
|
117
384
|
* credential's split configuration into the document -- one entry adding the
|
|
118
|
-
* credential's `keyAgreement` entry (verbatim or commitment)
|
|
119
|
-
*
|
|
120
|
-
* `
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
385
|
+
* credential's `keyAgreement` entry (verbatim or commitment), committing its
|
|
386
|
+
* current update key's hash in `nextKeyHashes`, and installing its LADDER VM
|
|
387
|
+
* under `assertionMethod` and `capabilityDelegation`, and under no other
|
|
388
|
+
* relation -- the asymmetry that recognizes it. The update key joins `updateKeys` nowhere.
|
|
389
|
+
* One entry for the whole inventory: a separate install would open a window
|
|
390
|
+
* in which the credential stands without the key it signs with.
|
|
391
|
+
*
|
|
392
|
+
* The ladder seed is the CALLER's to mint and to have already written
|
|
393
|
+
* durably. This function never mints one, so a torn bind's re-run tests
|
|
394
|
+
* idempotence against the SAME seed, finds the completed stage and publishes
|
|
395
|
+
* nothing -- where a mint-when-absent would publish a second VM that no
|
|
396
|
+
* anchored attribution could later strike. A credential with no ladder at all
|
|
397
|
+
* passes `null` and gets no VM.
|
|
398
|
+
*
|
|
399
|
+
* `part` splits the bind across two entries where a ceremony needs the
|
|
400
|
+
* credential's decryption material to precede its authority: `'key'`
|
|
401
|
+
* publishes the `keyAgreement` member alone, `'authority'` installs the
|
|
402
|
+
* ladder VM and commits the rung-0 hash, and `'all'` (the default) is the one
|
|
403
|
+
* merged entry every other caller writes.
|
|
404
|
+
*
|
|
405
|
+
* Idempotent: an inventory already published is a no-op, so re-running a torn
|
|
406
|
+
* bind converges. The entry publishes conditionally on the log this call
|
|
407
|
+
* read; a race lost to a concurrent ceremony re-runs and rebases on the new
|
|
408
|
+
* head.
|
|
124
409
|
*
|
|
125
410
|
* @param options {object}
|
|
126
411
|
* @param options.idStore {WebvhIdStore}
|
|
127
|
-
* @param options.
|
|
128
|
-
* did:webvh update-key seeds
|
|
412
|
+
* @param options.signer {AccountLogSigner} who signs the entry: the BINDING
|
|
413
|
+
* client's own did:webvh update-key seeds, or the acting credential's
|
|
414
|
+
* ladder seed
|
|
129
415
|
* @param options.unlockKeys {StandingUnlockKeys} the credential's public
|
|
130
416
|
* inventory
|
|
417
|
+
* @param options.ladderSeed {Uint8Array | null} the credential's ladder
|
|
418
|
+
* seed, whose VM this entry installs; `null` for a credential that carries
|
|
419
|
+
* no ladder
|
|
420
|
+
* @param [options.part] {string} `'all'` (the default), `'key'`, or
|
|
421
|
+
* `'authority'` -- see above
|
|
131
422
|
* @param [options.expectedDid] {string} the account DID the log must resolve
|
|
132
423
|
* to, from the caller's stored account pointer
|
|
133
424
|
* @param [options.pinStore] {ResourceLogPinStore} the caller's chain-head
|
|
@@ -163,25 +454,70 @@ export async function publishUnlockKey(options) {
|
|
|
163
454
|
* rung key still sitting in `updateKeys` (plus the never-claimed hashes its
|
|
164
455
|
* reveal entry committed). Trusting the recorded multibase alone would leave
|
|
165
456
|
* the live rung commitment standing: a latent re-seizure credential via the
|
|
166
|
-
* reveal mechanism.
|
|
167
|
-
*
|
|
168
|
-
*
|
|
457
|
+
* reveal mechanism. The seedless walk recovers the rungs BEHIND the anchor
|
|
458
|
+
* too, reading the log's positional rules backwards, so an anchor advanced by
|
|
459
|
+
* a self-enrollment resolves the same inventory a bind-time anchor does
|
|
460
|
+
* wherever each rung's hash was committed by an entry that also revealed the
|
|
461
|
+
* previous rung, or by a handover. One reachable shape falls outside that: a
|
|
462
|
+
* ladder VM the last-client transition reinstalled, whose acting rung a later
|
|
463
|
+
* self-enrollment then spends. That reveal-and-commit entry authorizes no key,
|
|
464
|
+
* so the backward walk cannot name the rung that signed it, and the VM stays
|
|
465
|
+
* standing as `unclaimed` (WC-158). A supplied `ladderSeed` is then a shortcut
|
|
466
|
+
* and a cross-check rather than a requirement (every rung known outright, no
|
|
467
|
+
* backward walk). For a
|
|
468
|
+
* single-key credential (a
|
|
169
469
|
* recovery code, a never-self-enrolled bind) the resolution degenerates to
|
|
170
470
|
* exactly the recorded key's hash, as before.
|
|
171
471
|
*
|
|
172
|
-
* The
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
472
|
+
* The credential's LADDER VM goes in the same entry, so a retired credential
|
|
473
|
+
* no longer signs governed-log appends or account delegations. This is the
|
|
474
|
+
* sole remover, and it needs no seed to do it: the VM is attributed from the
|
|
475
|
+
* log on any of three arms ({@link attributeLadderInventory}). The SIGNER
|
|
476
|
+
* arm claims a VM whose publishing entry a ladder rung signed. The
|
|
477
|
+
* CO-INTRODUCTION arm claims one whose publishing entry also introduced this
|
|
478
|
+
* credential's own `keyAgreement` member, which is what reaches a bind entry
|
|
479
|
+
* an enrolled client signed; it fires only when that entry introduced exactly
|
|
480
|
+
* one credential-class key-agreement member and exactly one ladder VM. The
|
|
481
|
+
* COMMITMENT arm claims one whose publishing entry committed a hash this
|
|
482
|
+
* ladder knows a priori and introduced no other credential's member, which is
|
|
483
|
+
* what reaches a reinstall for a credential whose member already stands. A VM
|
|
484
|
+
* no arm claims is left standing rather than struck -- on an account
|
|
485
|
+
* with several standing credentials, striking an unattributed key would take
|
|
486
|
+
* out a survivor's. With the seed in hand the derived id is struck too, and
|
|
487
|
+
* an attribution naming any OTHER VM refuses with
|
|
488
|
+
* {@link LadderAttributionError} rather than acting on a ladder the seed and
|
|
489
|
+
* the recorded anchor disagree about.
|
|
180
490
|
*
|
|
181
|
-
* @param options {object} see {@link publishUnlockKey}, plus
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
491
|
+
* @param options {object} see {@link publishUnlockKey}, plus:
|
|
492
|
+
* @param [options.ladderSeed] {Uint8Array} the retired credential's ladder
|
|
493
|
+
* seed, when in hand
|
|
494
|
+
* @param [options.projectionStore] {object} an `id`-collection store the
|
|
495
|
+
* caller may write through (a transient session's, bound to its generation
|
|
496
|
+
* delegation; an enrolled client's own root-invoking store). Supplied, the
|
|
497
|
+
* post-strike `did:web` projection is PUT through it immediately BEFORE
|
|
498
|
+
* this entry publishes, which is what keeps a ladder-signed removal from
|
|
499
|
+
* leaving `did.json` naming the retired credential. Best-effort: a failed
|
|
500
|
+
* PUT is warned and the removal proceeds. Omitted, the ladder arm leaves
|
|
501
|
+
* the projection to the next visit's `ensureDidWebProjection` and the
|
|
502
|
+
* client arm publishes it after the entry, as before
|
|
503
|
+
* @param [options.expectedLadderVmIds] {string[]} the ladder VM ids the
|
|
504
|
+
* caller already resolved for this credential, from its own read of the
|
|
505
|
+
* log. Supplied, this edit's own attribution must match them as a set --
|
|
506
|
+
* after the seed cross-check above -- or the edit refuses with
|
|
507
|
+
* {@link LadderInventoryDriftError} before writing anything. That is what
|
|
508
|
+
* ties the retirement's stage-0 read (whose list the dependent-record
|
|
509
|
+
* re-mint pass acted on) to this one, which is otherwise independent
|
|
510
|
+
* @param [options.requireLadderVmClaim] {boolean} the credential carries a
|
|
511
|
+
* ladder, so its VM must be claimed: the edit refuses with
|
|
512
|
+
* {@link UnclaimedLadderVmRetirementError} before writing when the claim
|
|
513
|
+
* struck nothing while ladder VMs stand unclaimed and the credential still
|
|
514
|
+
* stands ({@link assertLadderVmClaimed}). The retirement ceremony sets it,
|
|
515
|
+
* and so does a recovery code's removal, whose inventory carries the code's
|
|
516
|
+
* own ladder VM. The refusal names the recorded update key the walk was
|
|
517
|
+
* anchored on
|
|
518
|
+
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog, ladderVm:
|
|
519
|
+
* LadderVmRemovalReport }>} see {@link publishUnlockKey}, plus the ladder
|
|
520
|
+
* VM report: what this entry struck, and what stands unclaimed after it
|
|
185
521
|
*/
|
|
186
522
|
export async function removeUnlockKey(options) {
|
|
187
523
|
return withLogConflictRetry(() => setUnlockKeyInventoryOnce({ ...options, polarity: 'remove' }));
|
|
@@ -195,108 +531,216 @@ export async function removeUnlockKey(options) {
|
|
|
195
531
|
* @param options {object} see {@link publishUnlockKey}, plus `polarity`
|
|
196
532
|
* @returns {Promise<{ did: string, doc: DIDDoc, log: DIDLog }>}
|
|
197
533
|
*/
|
|
198
|
-
async function setUnlockKeyInventoryOnce({ idStore,
|
|
199
|
-
|
|
200
|
-
|
|
534
|
+
async function setUnlockKeyInventoryOnce({ idStore, signer, projectionStore, unlockKeys, ladderSeed, part = 'all', expectedLadderVmIds, requireLadderVmClaim, expectedDid, pinStore, logId, verb, polarity }) {
|
|
535
|
+
let ladderVmReport = { struck: [], unclaimed: [] };
|
|
536
|
+
const outcome = await signAccountEntry({
|
|
537
|
+
idStore,
|
|
538
|
+
signer,
|
|
201
539
|
...(expectedDid !== undefined ? { expectedDid } : {}),
|
|
202
540
|
...(pinStore ? { pinStore } : {}),
|
|
203
|
-
...(logId !== undefined ? { logId } : {})
|
|
541
|
+
...(logId !== undefined ? { logId } : {}),
|
|
542
|
+
missingMessage: 'did:webvh: did.jsonl is missing; nothing to enroll into.',
|
|
543
|
+
verb: verb ?? 'changing an unlock credential',
|
|
544
|
+
// The post-strike projection, published while the caller's store can
|
|
545
|
+
// still write it and before the entry the ladder arm cannot publish it
|
|
546
|
+
// with. Only the removal polarity is ever handed one: a publish adds
|
|
547
|
+
// inventory, which the served projection under-lists until some later
|
|
548
|
+
// writer refreshes it -- the safe direction.
|
|
549
|
+
...(projectionStore
|
|
550
|
+
? {
|
|
551
|
+
beforePublish: preEntryProjectionPublisher({
|
|
552
|
+
store: projectionStore
|
|
553
|
+
})
|
|
554
|
+
}
|
|
555
|
+
: {}),
|
|
556
|
+
build: async ({ published }) => {
|
|
557
|
+
const { did, doc } = published;
|
|
558
|
+
const keyHash = await deriveNextKeyHash(unlockKeys.updateKeyMultibase);
|
|
559
|
+
const vmId = unlockKeyVmId({ did, keyAgreement: unlockKeys.keyAgreement });
|
|
560
|
+
const vmPresent = (doc.verificationMethod ?? []).some(method => method.id === vmId);
|
|
561
|
+
// The remove polarity strikes the ladder's CURRENT inventory, resolved
|
|
562
|
+
// from the log with the recorded key as anchor -- never just the
|
|
563
|
+
// recorded key's hash, which a self-enrollment since the bind leaves
|
|
564
|
+
// stale (see {@link removeUnlockKey}). The credential's own
|
|
565
|
+
// verification-method id goes along: it is what tells the walk a climb
|
|
566
|
+
// from a spend, so the removal never annexes the commitment a spend
|
|
567
|
+
// handed to its replacement.
|
|
568
|
+
const inventory = polarity === 'remove'
|
|
569
|
+
? await attributeUnlockLadderInventory({
|
|
570
|
+
log: published.log,
|
|
571
|
+
did,
|
|
572
|
+
unlockKeys,
|
|
573
|
+
...(ladderSeed ? { ladderSeed } : {})
|
|
574
|
+
})
|
|
575
|
+
: { revealedKeys: [], committedHashes: [], ladderVmIds: [] };
|
|
576
|
+
const removedHashes = new Set(inventory.committedHashes);
|
|
577
|
+
const removedKeys = new Set(inventory.revealedKeys);
|
|
578
|
+
// The credential's ladder VM: installed by the publish polarity in this
|
|
579
|
+
// same entry, struck by the remove polarity in this same entry. The
|
|
580
|
+
// derived id is what the install publishes; the removal takes it from
|
|
581
|
+
// the seed when the ceremony holds one and from the log's attribution
|
|
582
|
+
// otherwise, so a seedless retirement still ends the credential's
|
|
583
|
+
// delegation authority.
|
|
584
|
+
const ladderVmKey = ladderSeed
|
|
585
|
+
? await ladderVmKeyMultibase({ ladderSeed })
|
|
586
|
+
: undefined;
|
|
587
|
+
const ladderVmId = ladderVmKey === undefined ? undefined : `${did}#${ladderVmKey}`;
|
|
588
|
+
const standingLadderVmIds = ladderVmIds({ doc });
|
|
589
|
+
const claim = polarity === 'remove'
|
|
590
|
+
? await ladderVmClaimOf({
|
|
591
|
+
doc,
|
|
592
|
+
did,
|
|
593
|
+
inventory,
|
|
594
|
+
...(ladderSeed ? { ladderSeed } : {})
|
|
595
|
+
})
|
|
596
|
+
: { struck: [], unclaimed: [] };
|
|
597
|
+
if (polarity === 'remove') {
|
|
598
|
+
// The drift check, before any write: this edit's own attribution
|
|
599
|
+
// against the list the caller resolved a read earlier. A concurrent
|
|
600
|
+
// ceremony, or a host serving two different log versions to the two
|
|
601
|
+
// reads, would otherwise let the strike diverge from what the
|
|
602
|
+
// caller's dependent-record pass already acted on.
|
|
603
|
+
if (expectedLadderVmIds !== undefined) {
|
|
604
|
+
const attributed = new Set(inventory.ladderVmIds);
|
|
605
|
+
const expected = new Set(expectedLadderVmIds);
|
|
606
|
+
const sameSet = attributed.size === expected.size &&
|
|
607
|
+
[...expected].every(id => attributed.has(id));
|
|
608
|
+
if (!sameSet) {
|
|
609
|
+
throw new LadderInventoryDriftError({
|
|
610
|
+
expected: [...expected],
|
|
611
|
+
attributed: [...attributed]
|
|
612
|
+
});
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
// The retirement gate, before any write and after the drift check: a
|
|
616
|
+
// ladder-carrying credential whose claim struck nothing while ladder
|
|
617
|
+
// VMs stand unclaimed is refused rather than retired with its VM left
|
|
618
|
+
// standing.
|
|
619
|
+
if (requireLadderVmClaim) {
|
|
620
|
+
await assertLadderVmClaimed({
|
|
621
|
+
log: published.log,
|
|
622
|
+
doc,
|
|
623
|
+
credentialVmId: vmId,
|
|
624
|
+
claim,
|
|
625
|
+
anchorKeyMultibase: unlockKeys.updateKeyMultibase
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
const struckLadderVmIds = new Set(claim.struck);
|
|
630
|
+
const ladderVmPresent = polarity === 'publish'
|
|
631
|
+
? ladderVmId !== undefined && standingLadderVmIds.includes(ladderVmId)
|
|
632
|
+
: struckLadderVmIds.size > 0;
|
|
633
|
+
const struckIds = new Set([vmId, ...struckLadderVmIds]);
|
|
634
|
+
const hashCommitted = published.nextKeyHashes.includes(keyHash);
|
|
635
|
+
// What this entry is responsible for, by `part`: the key half publishes
|
|
636
|
+
// the `keyAgreement` member alone, the authority half the ladder VM and
|
|
637
|
+
// the rung's commitment, and the default entry both.
|
|
638
|
+
const publishesKey = part === 'all' || part === 'key';
|
|
639
|
+
const publishesAuthority = part === 'all' || part === 'authority';
|
|
640
|
+
const settled = polarity === 'publish'
|
|
641
|
+
? (!publishesKey || vmPresent) &&
|
|
642
|
+
(!publishesAuthority ||
|
|
643
|
+
(hashCommitted && (ladderVmId === undefined || ladderVmPresent)))
|
|
644
|
+
: !vmPresent &&
|
|
645
|
+
!ladderVmPresent &&
|
|
646
|
+
removedHashes.size === 0 &&
|
|
647
|
+
removedKeys.size === 0;
|
|
648
|
+
ladderVmReport = {
|
|
649
|
+
struck: claim.struck,
|
|
650
|
+
unclaimed: claim.unclaimed
|
|
651
|
+
};
|
|
652
|
+
if (settled) {
|
|
653
|
+
return undefined;
|
|
654
|
+
}
|
|
655
|
+
const existingMethods = (doc.verificationMethod ??
|
|
656
|
+
[]);
|
|
657
|
+
// The publish polarity commits the credential's update-key hash through
|
|
658
|
+
// the seam's `commitHashes`, so on a ladder-signed entry it lands after
|
|
659
|
+
// the acting rung's own carry-over hash (`decisions/0007` order).
|
|
660
|
+
const commitHashes = polarity === 'publish' && publishesAuthority && !hashCommitted
|
|
661
|
+
? [keyHash]
|
|
662
|
+
: [];
|
|
663
|
+
const nextKeyHashes = polarity === 'publish'
|
|
664
|
+
? undefined
|
|
665
|
+
: published.nextKeyHashes.filter(hash => !removedHashes.has(hash));
|
|
666
|
+
// A torn self-enrollment leaves a revealed rung in `updateKeys`; the
|
|
667
|
+
// remove polarity strikes it in the same entry as its hash, keeping the
|
|
668
|
+
// carry-over invariant self-consistent. On the publish polarity and the
|
|
669
|
+
// ordinary committed-only removal this is the published set unchanged.
|
|
670
|
+
const statedUpdateKeys = polarity === 'publish'
|
|
671
|
+
? undefined
|
|
672
|
+
: published.updateKeys.filter(key => !removedKeys.has(key));
|
|
673
|
+
const installedLadderVmKey = publishesAuthority ? ladderVmKey : undefined;
|
|
674
|
+
const verificationMethods = polarity === 'publish'
|
|
675
|
+
? [
|
|
676
|
+
...existingMethods.filter(method => method.id !== (publishesKey ? vmId : undefined) &&
|
|
677
|
+
method.id !== (publishesAuthority ? ladderVmId : undefined)),
|
|
678
|
+
...(publishesKey
|
|
679
|
+
? [
|
|
680
|
+
unlockKeyVerificationMethod({
|
|
681
|
+
did,
|
|
682
|
+
keyAgreement: unlockKeys.keyAgreement
|
|
683
|
+
})
|
|
684
|
+
]
|
|
685
|
+
: []),
|
|
686
|
+
...(installedLadderVmKey !== undefined
|
|
687
|
+
? [
|
|
688
|
+
ladderVerificationMethod({
|
|
689
|
+
controller: did,
|
|
690
|
+
publicKeyMultibase: installedLadderVmKey
|
|
691
|
+
})
|
|
692
|
+
]
|
|
693
|
+
: [])
|
|
694
|
+
]
|
|
695
|
+
: existingMethods.filter(method => method.id === undefined || !struckIds.has(method.id));
|
|
696
|
+
// On the remove polarity every relation drops the struck ids: the
|
|
697
|
+
// credential's entry sits under `keyAgreement` alone and the ladder VM
|
|
698
|
+
// under `assertionMethod` and `capabilityDelegation` alone, so one
|
|
699
|
+
// filter serves all five without restating either placement here.
|
|
700
|
+
const relation = (ids) => polarity === 'publish'
|
|
701
|
+
? relationIds(ids)
|
|
702
|
+
: relationIds(ids).filter(id => !struckIds.has(id));
|
|
703
|
+
const keyAgreement = polarity === 'publish'
|
|
704
|
+
? publishesKey
|
|
705
|
+
? [...new Set([...relationIds(doc.keyAgreement), vmId])]
|
|
706
|
+
: relationIds(doc.keyAgreement)
|
|
707
|
+
: relation(doc.keyAgreement);
|
|
708
|
+
// The ladder VM's two relations, and only those: the asymmetry is what
|
|
709
|
+
// recognizes it (`ladderVmIds`) and what keeps it out of every client
|
|
710
|
+
// listing.
|
|
711
|
+
const withLadderVm = (ids) => installedLadderVmKey === undefined || ladderVmId === undefined
|
|
712
|
+
? relationIds(ids)
|
|
713
|
+
: [...new Set([...relationIds(ids), ladderVmId])];
|
|
714
|
+
const assertionMethod = polarity === 'publish'
|
|
715
|
+
? withLadderVm(doc.assertionMethod)
|
|
716
|
+
: relation(doc.assertionMethod);
|
|
717
|
+
const capabilityDelegation = polarity === 'publish'
|
|
718
|
+
? withLadderVm(doc.capabilityDelegation)
|
|
719
|
+
: relation(doc.capabilityDelegation);
|
|
720
|
+
return {
|
|
721
|
+
// The byoe context that defines a commitment entry's terms is
|
|
722
|
+
// installed at genesis and carried forward by every update, so no
|
|
723
|
+
// edit re-appends it.
|
|
724
|
+
...(statedUpdateKeys !== undefined
|
|
725
|
+
? { updateKeys: statedUpdateKeys }
|
|
726
|
+
: {}),
|
|
727
|
+
...(nextKeyHashes !== undefined ? { nextKeyHashes } : {}),
|
|
728
|
+
...(commitHashes.length > 0 ? { commitHashes } : {}),
|
|
729
|
+
verificationMethods,
|
|
730
|
+
authentication: relation(doc.authentication),
|
|
731
|
+
assertionMethod,
|
|
732
|
+
keyAgreement,
|
|
733
|
+
capabilityInvocation: relation(doc.capabilityInvocation),
|
|
734
|
+
capabilityDelegation
|
|
735
|
+
};
|
|
736
|
+
}
|
|
204
737
|
});
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
// {@link removeUnlockKey}). The credential's own verification-method id
|
|
213
|
-
// goes along: it is what tells the walk a climb from a spend, so the
|
|
214
|
-
// removal never annexes the commitment a spend handed to its replacement.
|
|
215
|
-
const inventory = polarity === 'remove'
|
|
216
|
-
? await attributeLadderInventory({
|
|
217
|
-
log: published.log,
|
|
218
|
-
anchorKeyMultibase: unlockKeys.updateKeyMultibase,
|
|
219
|
-
credentialVmId: vmId,
|
|
220
|
-
...(ladderSeed ? { ladderSeed } : {})
|
|
221
|
-
})
|
|
222
|
-
: { revealedKeys: [], committedHashes: [] };
|
|
223
|
-
const removedHashes = new Set(inventory.committedHashes);
|
|
224
|
-
const removedKeys = new Set(inventory.revealedKeys);
|
|
225
|
-
// The credential's ladder VM, where one stands: the stable sibling a
|
|
226
|
-
// last-client forget's install entry publishes, which stays behind when
|
|
227
|
-
// that forget is torn after its first entry. It is derived from the ladder
|
|
228
|
-
// seed and nothing else, so ownership is attributable only with the seed in
|
|
229
|
-
// hand; a removal without it leaves the sibling standing (see
|
|
230
|
-
// {@link removeUnlockKey}).
|
|
231
|
-
const ladderVmId = polarity === 'remove' && ladderSeed
|
|
232
|
-
? `${did}#${await ladderVmKeyMultibase({ ladderSeed })}`
|
|
233
|
-
: undefined;
|
|
234
|
-
const ladderVmPresent = ladderVmId !== undefined && ladderVmIds({ doc }).includes(ladderVmId);
|
|
235
|
-
const struckIds = new Set([vmId, ...(ladderVmPresent ? [ladderVmId] : [])]);
|
|
236
|
-
const hashCommitted = published.nextKeyHashes.includes(keyHash);
|
|
237
|
-
const settled = polarity === 'publish'
|
|
238
|
-
? vmPresent && hashCommitted
|
|
239
|
-
: !vmPresent &&
|
|
240
|
-
!ladderVmPresent &&
|
|
241
|
-
removedHashes.size === 0 &&
|
|
242
|
-
removedKeys.size === 0;
|
|
243
|
-
if (settled) {
|
|
244
|
-
return { did, doc, log: published.log };
|
|
245
|
-
}
|
|
246
|
-
const activeKey = await updateKeyMultibase({ seed: updateKeys.updateSeed });
|
|
247
|
-
if (!published.updateKeys.includes(activeKey)) {
|
|
248
|
-
throw new Error("did:webvh: the published log does not authorize this client's active " +
|
|
249
|
-
'update key; finalize the pending rotation before ' +
|
|
250
|
-
`${verb ?? 'changing an unlock credential'}.`);
|
|
251
|
-
}
|
|
252
|
-
await assertCarryOverCommitments({ published });
|
|
253
|
-
const existingMethods = (doc.verificationMethod ?? []);
|
|
254
|
-
const signer = await updateKeySigner({ seed: updateKeys.updateSeed });
|
|
255
|
-
const nextKeyHashes = polarity === 'publish'
|
|
256
|
-
? [...new Set([...published.nextKeyHashes, keyHash])]
|
|
257
|
-
: published.nextKeyHashes.filter(hash => !removedHashes.has(hash));
|
|
258
|
-
// A torn self-enrollment leaves a revealed rung in `updateKeys`; the remove
|
|
259
|
-
// polarity strikes it in the same entry as its hash, keeping the carry-over
|
|
260
|
-
// invariant self-consistent. On the publish polarity and the ordinary
|
|
261
|
-
// committed-only removal this is the published set unchanged.
|
|
262
|
-
const statedUpdateKeys = polarity === 'publish'
|
|
263
|
-
? published.updateKeys
|
|
264
|
-
: published.updateKeys.filter(key => !removedKeys.has(key));
|
|
265
|
-
const verificationMethods = polarity === 'publish'
|
|
266
|
-
? [
|
|
267
|
-
...existingMethods.filter(method => method.id !== vmId),
|
|
268
|
-
unlockKeyVerificationMethod({
|
|
269
|
-
did,
|
|
270
|
-
keyAgreement: unlockKeys.keyAgreement
|
|
271
|
-
})
|
|
272
|
-
]
|
|
273
|
-
: existingMethods.filter(method => method.id === undefined || !struckIds.has(method.id));
|
|
274
|
-
// On the remove polarity every relation drops the struck ids: the
|
|
275
|
-
// credential's entry sits under `keyAgreement` alone and the ladder VM under
|
|
276
|
-
// `assertionMethod` and `capabilityDelegation` alone, so one filter serves
|
|
277
|
-
// all five without restating either placement here.
|
|
278
|
-
const relation = (ids) => polarity === 'publish'
|
|
279
|
-
? relationIds(ids)
|
|
280
|
-
: relationIds(ids).filter(id => !struckIds.has(id));
|
|
281
|
-
const keyAgreement = polarity === 'publish'
|
|
282
|
-
? [...new Set([...relationIds(doc.keyAgreement), vmId])]
|
|
283
|
-
: relation(doc.keyAgreement);
|
|
284
|
-
const updated = await updateDID({
|
|
285
|
-
log: published.log,
|
|
286
|
-
signer,
|
|
287
|
-
alsoKnownAsWeb: true,
|
|
288
|
-
// The byoe context that defines a commitment entry's terms is installed
|
|
289
|
-
// at genesis and carried forward by every update, so no edit re-appends it.
|
|
290
|
-
updateKeys: statedUpdateKeys,
|
|
291
|
-
nextKeyHashes,
|
|
292
|
-
verificationMethods,
|
|
293
|
-
authentication: relation(doc.authentication),
|
|
294
|
-
assertionMethod: relation(doc.assertionMethod),
|
|
295
|
-
keyAgreement,
|
|
296
|
-
capabilityInvocation: relation(doc.capabilityInvocation),
|
|
297
|
-
capabilityDelegation: relation(doc.capabilityDelegation)
|
|
298
|
-
});
|
|
299
|
-
await publishUpdatedLog({ idStore, updated, ifMatch: published.etag });
|
|
300
|
-
return { did: updated.did, doc: updated.doc, log: updated.log };
|
|
738
|
+
const settledHead = outcome.updated ?? outcome.published;
|
|
739
|
+
return {
|
|
740
|
+
did: settledHead.did,
|
|
741
|
+
doc: settledHead.doc,
|
|
742
|
+
log: settledHead.log,
|
|
743
|
+
ladderVm: ladderVmReport
|
|
744
|
+
};
|
|
301
745
|
}
|
|
302
746
|
//# sourceMappingURL=standingWebvh.js.map
|