@real-life/trust-protocol 0.3.2 → 0.4.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/CHANGELOG.md +32 -0
- package/README.md +187 -78
- package/dist/ack.d.ts +84 -0
- package/dist/ack.d.ts.map +1 -0
- package/dist/ack.js +114 -0
- package/dist/ack.js.map +1 -0
- package/dist/adapters/vti-mediator.d.ts +154 -0
- package/dist/adapters/vti-mediator.d.ts.map +1 -0
- package/dist/adapters/vti-mediator.js +621 -0
- package/dist/adapters/vti-mediator.js.map +1 -0
- package/dist/anchor.d.ts +226 -0
- package/dist/anchor.d.ts.map +1 -0
- package/dist/anchor.js +347 -0
- package/dist/anchor.js.map +1 -0
- package/dist/{holder.d.ts → carrier-holder.d.ts} +1 -1
- package/dist/carrier-holder.d.ts.map +1 -0
- package/dist/{holder.js → carrier-holder.js} +5 -4
- package/dist/carrier-holder.js.map +1 -0
- package/dist/carrier-identity.js +4 -4
- package/dist/carrier-identity.js.map +1 -1
- package/dist/carrier.d.ts.map +1 -1
- package/dist/carrier.js +4 -3
- package/dist/carrier.js.map +1 -1
- package/dist/ceremony.d.ts +3 -2
- package/dist/ceremony.d.ts.map +1 -1
- package/dist/ceremony.js +16 -26
- package/dist/ceremony.js.map +1 -1
- package/dist/codec.d.ts +46 -0
- package/dist/codec.d.ts.map +1 -0
- package/dist/codec.js +410 -0
- package/dist/codec.js.map +1 -0
- package/dist/continuity.d.ts +67 -0
- package/dist/continuity.d.ts.map +1 -0
- package/dist/continuity.js +110 -0
- package/dist/continuity.js.map +1 -0
- package/dist/core.d.ts +4 -0
- package/dist/core.d.ts.map +1 -1
- package/dist/core.js.map +1 -1
- package/dist/crypto.d.ts +23 -7
- package/dist/crypto.d.ts.map +1 -1
- package/dist/crypto.js +54 -20
- package/dist/crypto.js.map +1 -1
- package/dist/delivery.d.ts +31 -3
- package/dist/delivery.d.ts.map +1 -1
- package/dist/delivery.js +75 -28
- package/dist/delivery.js.map +1 -1
- package/dist/encounter/ceremony.d.ts +37 -0
- package/dist/encounter/ceremony.d.ts.map +1 -0
- package/dist/encounter/ceremony.js +662 -0
- package/dist/encounter/ceremony.js.map +1 -0
- package/dist/encounter.d.ts +91 -1
- package/dist/encounter.d.ts.map +1 -1
- package/dist/encounter.js +191 -12
- package/dist/encounter.js.map +1 -1
- package/dist/engine.d.ts +291 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +1395 -0
- package/dist/engine.js.map +1 -0
- package/dist/entropy.d.ts +28 -0
- package/dist/entropy.d.ts.map +1 -0
- package/dist/entropy.js +96 -0
- package/dist/entropy.js.map +1 -0
- package/dist/events.d.ts +177 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +2 -0
- package/dist/events.js.map +1 -0
- package/dist/fixtures.d.ts +30 -0
- package/dist/fixtures.d.ts.map +1 -0
- package/dist/fixtures.js +155 -0
- package/dist/fixtures.js.map +1 -0
- package/dist/forge.d.ts +47 -0
- package/dist/forge.d.ts.map +1 -0
- package/dist/forge.js +20 -0
- package/dist/forge.js.map +1 -0
- package/dist/group-star.d.ts +251 -0
- package/dist/group-star.d.ts.map +1 -0
- package/dist/group-star.js +348 -0
- package/dist/group-star.js.map +1 -0
- package/dist/holder-api.d.ts +137 -0
- package/dist/holder-api.d.ts.map +1 -0
- package/dist/holder-api.js +132 -0
- package/dist/holder-api.js.map +1 -0
- package/dist/holder-state-v1.d.ts +801 -0
- package/dist/holder-state-v1.d.ts.map +1 -0
- package/dist/holder-state-v1.js +2 -0
- package/dist/holder-state-v1.js.map +1 -0
- package/dist/identity-state.d.ts +43 -0
- package/dist/identity-state.d.ts.map +1 -0
- package/dist/identity-state.js +345 -0
- package/dist/identity-state.js.map +1 -0
- package/dist/identity.d.ts +24 -2
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +70 -11
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +26 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +77 -29
- package/dist/index.js.map +1 -1
- package/dist/internal.d.ts +36 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +22 -0
- package/dist/internal.js.map +1 -0
- package/dist/introduction/holder.d.ts +18 -0
- package/dist/introduction/holder.d.ts.map +1 -0
- package/dist/introduction/holder.js +757 -0
- package/dist/introduction/holder.js.map +1 -0
- package/dist/introduction.d.ts +101 -0
- package/dist/introduction.d.ts.map +1 -0
- package/dist/introduction.js +132 -0
- package/dist/introduction.js.map +1 -0
- package/dist/invariants.d.ts +43 -0
- package/dist/invariants.d.ts.map +1 -0
- package/dist/invariants.js +883 -0
- package/dist/invariants.js.map +1 -0
- package/dist/keys-seed.d.ts +7 -0
- package/dist/keys-seed.d.ts.map +1 -0
- package/dist/keys-seed.js +133 -0
- package/dist/keys-seed.js.map +1 -0
- package/dist/log-memory.d.ts +12 -0
- package/dist/log-memory.d.ts.map +1 -0
- package/dist/log-memory.js +101 -0
- package/dist/log-memory.js.map +1 -0
- package/dist/membership/holder.d.ts +35 -0
- package/dist/membership/holder.d.ts.map +1 -0
- package/dist/membership/holder.js +852 -0
- package/dist/membership/holder.js.map +1 -0
- package/dist/membership.d.ts +92 -0
- package/dist/membership.d.ts.map +1 -0
- package/dist/membership.js +157 -0
- package/dist/membership.js.map +1 -0
- package/dist/ports.d.ts +237 -0
- package/dist/ports.d.ts.map +1 -0
- package/dist/ports.js +2 -0
- package/dist/ports.js.map +1 -0
- package/dist/profile.d.ts +56 -0
- package/dist/profile.d.ts.map +1 -0
- package/dist/profile.js +193 -0
- package/dist/profile.js.map +1 -0
- package/dist/prompts.d.ts +14 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +69 -0
- package/dist/prompts.js.map +1 -0
- package/dist/receive.d.ts +133 -0
- package/dist/receive.d.ts.map +1 -0
- package/dist/receive.js +298 -0
- package/dist/receive.js.map +1 -0
- package/dist/results.d.ts +158 -0
- package/dist/results.d.ts.map +1 -0
- package/dist/results.js +21 -0
- package/dist/results.js.map +1 -0
- package/dist/schemas.d.ts.map +1 -1
- package/dist/schemas.js +42 -38
- package/dist/schemas.js.map +1 -1
- package/dist/sha256.d.ts +3 -0
- package/dist/sha256.d.ts.map +1 -0
- package/dist/sha256.js +69 -0
- package/dist/sha256.js.map +1 -0
- package/dist/star.d.ts +31 -0
- package/dist/star.d.ts.map +1 -0
- package/dist/star.js +56 -0
- package/dist/star.js.map +1 -0
- package/dist/state-builder.d.ts +42 -0
- package/dist/state-builder.d.ts.map +1 -0
- package/dist/state-builder.js +100 -0
- package/dist/state-builder.js.map +1 -0
- package/dist/state.d.ts +853 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +36 -0
- package/dist/state.js.map +1 -0
- package/dist/store-contract.d.ts +11 -0
- package/dist/store-contract.d.ts.map +1 -0
- package/dist/store-contract.js +189 -0
- package/dist/store-contract.js.map +1 -0
- package/dist/store-memory.d.ts +8 -0
- package/dist/store-memory.d.ts.map +1 -0
- package/dist/store-memory.js +17 -0
- package/dist/store-memory.js.map +1 -0
- package/dist/testing.d.ts +22 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +21 -0
- package/dist/testing.js.map +1 -0
- package/dist/trust/continuity.d.ts +71 -0
- package/dist/trust/continuity.d.ts.map +1 -0
- package/dist/trust/continuity.js +1216 -0
- package/dist/trust/continuity.js.map +1 -0
- package/dist/trust/duties.d.ts +67 -0
- package/dist/trust/duties.d.ts.map +1 -0
- package/dist/trust/duties.js +179 -0
- package/dist/trust/duties.js.map +1 -0
- package/dist/trust/entries.d.ts +139 -0
- package/dist/trust/entries.d.ts.map +1 -0
- package/dist/trust/entries.js +694 -0
- package/dist/trust/entries.js.map +1 -0
- package/dist/trust/group-star.d.ts +43 -0
- package/dist/trust/group-star.d.ts.map +1 -0
- package/dist/trust/group-star.js +188 -0
- package/dist/trust/group-star.js.map +1 -0
- package/dist/trust/mapping.d.ts +56 -0
- package/dist/trust/mapping.d.ts.map +1 -0
- package/dist/trust/mapping.js +584 -0
- package/dist/trust/mapping.js.map +1 -0
- package/dist/trust/profile.d.ts +25 -0
- package/dist/trust/profile.d.ts.map +1 -0
- package/dist/trust/profile.js +249 -0
- package/dist/trust/profile.js.map +1 -0
- package/dist/trust/star.d.ts +208 -0
- package/dist/trust/star.d.ts.map +1 -0
- package/dist/trust/star.js +588 -0
- package/dist/trust/star.js.map +1 -0
- package/dist/trust/state.d.ts +83 -0
- package/dist/trust/state.d.ts.map +1 -0
- package/dist/trust/state.js +154 -0
- package/dist/trust/state.js.map +1 -0
- package/dist/util-freeze.d.ts +3 -0
- package/dist/util-freeze.d.ts.map +1 -0
- package/dist/util-freeze.js +11 -0
- package/dist/util-freeze.js.map +1 -0
- package/dist/views.d.ts +147 -0
- package/dist/views.d.ts.map +1 -0
- package/dist/views.js +268 -0
- package/dist/views.js.map +1 -0
- package/dist/visibility.d.ts +5 -5
- package/dist/visibility.d.ts.map +1 -1
- package/dist/visibility.js +22 -28
- package/dist/visibility.js.map +1 -1
- package/dist/wire.d.ts +178 -84
- package/dist/wire.d.ts.map +1 -1
- package/dist/wire.js.map +1 -1
- package/package.json +56 -10
- package/schemas/holder-state-v1.schema.json +3880 -0
- package/dist/holder.d.ts.map +0 -1
- package/dist/holder.js.map +0 -1
- package/dist/probe/acks.d.ts +0 -27
- package/dist/probe/acks.d.ts.map +0 -1
- package/dist/probe/acks.js +0 -152
- package/dist/probe/acks.js.map +0 -1
- package/dist/probe/continuity.d.ts +0 -20
- package/dist/probe/continuity.d.ts.map +0 -1
- package/dist/probe/continuity.js +0 -805
- package/dist/probe/continuity.js.map +0 -1
- package/dist/probe/deps.d.ts +0 -61
- package/dist/probe/deps.d.ts.map +0 -1
- package/dist/probe/deps.js +0 -54
- package/dist/probe/deps.js.map +0 -1
- package/dist/probe/encounter.d.ts +0 -26
- package/dist/probe/encounter.d.ts.map +0 -1
- package/dist/probe/encounter.js +0 -938
- package/dist/probe/encounter.js.map +0 -1
- package/dist/probe/introduce.d.ts +0 -62
- package/dist/probe/introduce.d.ts.map +0 -1
- package/dist/probe/introduce.js +0 -306
- package/dist/probe/introduce.js.map +0 -1
- package/dist/probe/membership.d.ts +0 -96
- package/dist/probe/membership.d.ts.map +0 -1
- package/dist/probe/membership.js +0 -467
- package/dist/probe/membership.js.map +0 -1
- package/dist/probe/trust.d.ts +0 -156
- package/dist/probe/trust.d.ts.map +0 -1
- package/dist/probe/trust.js +0 -1233
- package/dist/probe/trust.js.map +0 -1
- package/dist/probe.d.ts +0 -7
- package/dist/probe.d.ts.map +0 -1
- package/dist/probe.js +0 -42
- package/dist/probe.js.map +0 -1
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
// trust/mapping — the Trust Layer's anchor mapping on the holder (lib
|
|
2
|
+
// Stage 2, PR 4a): the trust act and the class settings (Section 2a), the
|
|
3
|
+
// issuance of anchor-mapping@4 (6.1, 6300/6310, 6420), its verification
|
|
4
|
+
// (6.3) and the entry commit of the deliverable set (Section 2), on
|
|
5
|
+
// HolderState and the engine.
|
|
6
|
+
//
|
|
7
|
+
// Rule numbers: bare four-digit numbers are RLTP-TRU (trust-layer.md).
|
|
8
|
+
//
|
|
9
|
+
// What the module registers (createHolder builds it in):
|
|
10
|
+
// commands trust.set {contactId, on} the Trust button: the
|
|
11
|
+
// anchor mapping class `on` (2770) or `off` (2840); human
|
|
12
|
+
// trust.setClass {contactId, cls, setting} 2780/2810; human
|
|
13
|
+
// trust.setGroupClass {contactId, groupId, setting} 2830; human
|
|
14
|
+
// trust.pause {contactId, paused} the pause of 5.4: star
|
|
15
|
+
// and blinded group star `off`, or back to `default`; human
|
|
16
|
+
// trust.recover {} 2240: entries re-commit
|
|
17
|
+
// from verified mappings recovered without them; non-human
|
|
18
|
+
// task anchor-mapping/0.3 (proof absent): conditions 1–7 in the
|
|
19
|
+
// preparation (stage 8), condition 8 and the transition in
|
|
20
|
+
// apply (stage 9): mappingIn on the arrival tuple, links and
|
|
21
|
+
// bindings at chain level (6610), the counterpart head, the
|
|
22
|
+
// entry commit (2060) or its update in place (2070)
|
|
23
|
+
// duties class-on/1 (2850) and reissue-mapping/1 (6370, 6596): issue
|
|
24
|
+
// the current anchor-mapping@4 on one relationship; class-on of
|
|
25
|
+
// the star classes is discharged by the reconciliation (5.4);
|
|
26
|
+
// class-on of the profile and profile/1 (6455) issue profile@1
|
|
27
|
+
// (trust/profile.ts, PR 6)
|
|
28
|
+
// and, from trust/star.ts and trust/group-star.ts (PR 4b): the tasks
|
|
29
|
+
// star/0.2 and group-star/0.1, the sweeps trust.star and
|
|
30
|
+
// trust.groupStar run by resume, and their ack handlers
|
|
31
|
+
// and, from trust/profile.ts (PR 6): the command profile.set (human),
|
|
32
|
+
// the task profile/0.1 and the profile's duties
|
|
33
|
+
// and, from trust/entries.ts (PR 4c): the commands trust.merge and
|
|
34
|
+
// trust.depart and the duty merge/1; from trust/continuity.ts
|
|
35
|
+
// (PR 4c): the tasks continuity-probe/0.1 and
|
|
36
|
+
// continuity-mapping/0.1, the duty continuity/1, their ack and
|
|
37
|
+
// failure handlers
|
|
38
|
+
//
|
|
39
|
+
// Choices this file makes where note and spec leave them open:
|
|
40
|
+
// - a relationship's verifications go to the entry its active tuple's
|
|
41
|
+
// entryId names (2145), else the one its identity (contactId) names
|
|
42
|
+
// (trust/state.ts entryOf); it sends under that entry's settings
|
|
43
|
+
// (2146). An entry whose relationships hold incomparable anchor heads
|
|
44
|
+
// (a manual merge, 6640, before its split) keeps its anchor head where
|
|
45
|
+
// it is among the newest, else takes the verifying relationship's head
|
|
46
|
+
// - an accepted mapping runs, in order: the split of 2080 (the first
|
|
47
|
+
// mapping on a hand-merged relationship that shares no anchor with
|
|
48
|
+
// the entry), the entry commit, and the merge duties of the net and of
|
|
49
|
+
// 2145 (trust/entries.ts), which merge after this commit (2230)
|
|
50
|
+
// - mappingIn.body and mappingOut.body hold the mapping's body (the
|
|
51
|
+
// artifact without its MACs): the revision rule compares bodies (6270),
|
|
52
|
+
// 6310 compares the issued body
|
|
53
|
+
// - an equal revision with an identical body (6270 repeat, a resend under
|
|
54
|
+
// a new document id) is acknowledged and changes nothing, except that
|
|
55
|
+
// it re-commits an entry lost with state loss (2240); a lower revision,
|
|
56
|
+
// an equivocation and an unlinked change of self (6170) are
|
|
57
|
+
// failed(validation-failed) at stage 9 with the rule as ref
|
|
58
|
+
// - a document carrying ceremony.enactment is failed(validation-failed):
|
|
59
|
+
// a mapping has no material to recompute it against (Delivery §3)
|
|
60
|
+
// - trust.received is announced at the entry commit of a verification,
|
|
61
|
+
// contact.verified at every verification that changed the state; the
|
|
62
|
+
// re-commit of trust.recover (no verification) announces nothing
|
|
63
|
+
// - the admission bound is the constant 10¹⁸−1 (2050); trustModule takes
|
|
64
|
+
// another bound for tests only, as the probe's admissionBound did
|
|
65
|
+
// - an entry committed at a full set is deliverable-pending (2250);
|
|
66
|
+
// trust.recover, a merge and a departure admit pending entries in
|
|
67
|
+
// position order while the set has room (trust/entries.ts)
|
|
68
|
+
// - the entry commit of 2240 runs on trust.recover and on any accepted
|
|
69
|
+
// mapping of the relationship, the repeat included
|
|
70
|
+
// - issuance needs a community anchor (register.community): without one
|
|
71
|
+
// the Trust act is refused as no-community, a duty stays refused
|
|
72
|
+
// - the community key agrees through the KeyProvider under a release the
|
|
73
|
+
// module derives from the register (the current generation's label),
|
|
74
|
+
// not from `addresses`: it is no delivery address (Delivery §5)
|
|
75
|
+
// - the class duties (2850, 6410, 6595, 6596) open, drop and move by the
|
|
76
|
+
// one follow rule of trust/duties.ts, run after every settings change,
|
|
77
|
+
// verification, merge, split, departure and chaining. A class-on duty
|
|
78
|
+
// of the anchor mapping finding the class off, its relationship gone
|
|
79
|
+
// or lagging is fulfilled without sending; ceasing to lag opens a new
|
|
80
|
+
// one (2850). A re-issue duty sends whether or not the relationship
|
|
81
|
+
// lags now (6596) and is dropped while the class is off (6410)
|
|
82
|
+
// - every issuance on a relationship fulfils every open class-on duty of
|
|
83
|
+
// the anchor mapping and every re-issue owed on it (6596: one delivery
|
|
84
|
+
// meets every re-issue still owed)
|
|
85
|
+
// - the star, the blinded and the sealed group star are delivered by the
|
|
86
|
+
// reconciliation of 5.4 (trust/star.ts), which sees every settings
|
|
87
|
+
// change through its view, so changing their settings opens no class-on
|
|
88
|
+
// duty; a class-on duty of one of them (from elsewhere) is fulfilled
|
|
89
|
+
// without sending, since the sweep runs in the same resume after the
|
|
90
|
+
// duties. The profile's class-on and revision duties issue profile@1
|
|
91
|
+
// (trust/profile.ts)
|
|
92
|
+
// - trust.pause sets star and blinded group star `off`, unpausing back to
|
|
93
|
+
// `default`; it needs no trust (2780)
|
|
94
|
+
// - setGroupClass to `default` removes the per-group entry; turning the
|
|
95
|
+
// sealed group star class `off` resets every per-group `on` (5450), on
|
|
96
|
+
// that transition only: a group set `on` afterwards stays, and setting
|
|
97
|
+
// `off` while it is off resets nothing
|
|
98
|
+
import { jcs, iso, makeValidator, calOK } from '../core.js';
|
|
99
|
+
import { digestDoc, xRawOfMk } from '../crypto.js';
|
|
100
|
+
import { seal } from '../delivery.js';
|
|
101
|
+
import { communityLabel } from '../identity.js';
|
|
102
|
+
import { SCHEMAS } from '../schemas.js';
|
|
103
|
+
import { LINEAGE_MAX, MAPPING_CONDITION_RULE, assembleAnchorMapping, bindingOf, classifyMapping, lineageLinks, mappingBindings, rawProofValue, verifyAnchorMappingWith, } from '../anchor.js';
|
|
104
|
+
import { tupleByRkid } from '../receive.js';
|
|
105
|
+
import { ADMISSION_BOUND, CLASSES, activeTuple, classOnToward, entryOf, entryRelationships, findContact, lags, liveRel, unionBindings, } from './state.js';
|
|
106
|
+
import { admitPending, entryCommands, mergeDutiesAfter, mergeDuty, settleHead, splitIfUnlinked } from './entries.js';
|
|
107
|
+
import { classOnHandler, followClasses, owedOn } from './duties.js';
|
|
108
|
+
import { profileIssueDuty, profileParts } from './profile.js';
|
|
109
|
+
import { ASSEMBLY_LIMIT, STAR, STAR_TYPE, ackOf, rematchStars, retainInflight, starBlinds, sweepOf, taskOf } from './star.js';
|
|
110
|
+
import { GROUP_STAR, GROUP_STAR_TYPE } from './group-star.js';
|
|
111
|
+
import { continuityParts } from './continuity.js';
|
|
112
|
+
const te = new TextEncoder();
|
|
113
|
+
const V = makeValidator(SCHEMAS);
|
|
114
|
+
const mappingSchemaOk = (m) => V.validate(m, SCHEMAS['visibility-anchor-mapping.schema.json'], SCHEMAS['visibility-anchor-mapping.schema.json']).length === 0;
|
|
115
|
+
/** The registered task type that carries anchor-mapping@4 (Delivery §4.4). */
|
|
116
|
+
export const ANCHOR_MAPPING_TYPE = 'https://real-life.org/trust-tasks/anchor-mapping/0.3';
|
|
117
|
+
/** The trust module's command names. */
|
|
118
|
+
export const TRUST_COMMANDS = Object.freeze({
|
|
119
|
+
set: 'trust.set', setClass: 'trust.setClass', setGroupClass: 'trust.setGroupClass', pause: 'trust.pause', recover: 'trust.recover',
|
|
120
|
+
});
|
|
121
|
+
const SETTINGS = ['default', 'on', 'off'];
|
|
122
|
+
const inc = (d) => (BigInt(d) + 1n).toString();
|
|
123
|
+
/** The current generation's label and the next one's (Identity §5.4). */
|
|
124
|
+
function communityOf(state) {
|
|
125
|
+
const c = state.register.community;
|
|
126
|
+
if (c === null)
|
|
127
|
+
return null;
|
|
128
|
+
const gen = Number(c.head);
|
|
129
|
+
if (!Number.isSafeInteger(gen) || gen < 1 || gen + 1 > Number.MAX_SAFE_INTEGER)
|
|
130
|
+
throw new Error(`trust: community head ${c.head} outside [1, 2^53 − 2] (Identity §5.4)`);
|
|
131
|
+
return { genesis: c.genesis, gen, label: communityLabel(c.genesis, gen), nextLabel: communityLabel(c.genesis, gen + 1) };
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* The lineage a mapping on one relationship carries (6020, 6420): empty
|
|
135
|
+
* where nothing was issued there under an earlier anchor, otherwise the
|
|
136
|
+
* holder's lineage from the element that leaves the relationship's
|
|
137
|
+
* earliest anchor (2285) to `self`, at most the LINEAGE_MAX most recent.
|
|
138
|
+
*/
|
|
139
|
+
export function lineageSegment(lineage, earliest, self) {
|
|
140
|
+
if (earliest === null || earliest === self)
|
|
141
|
+
return [];
|
|
142
|
+
const at = lineage.findIndex((r) => r?.body?.prev === earliest);
|
|
143
|
+
if (at < 0)
|
|
144
|
+
throw new Error('trust: a relationship\'s earliest anchor is not in the register lineage (2285, 6420)');
|
|
145
|
+
const seg = lineage.slice(at);
|
|
146
|
+
if (seg[seg.length - 1].body.next !== self)
|
|
147
|
+
throw new Error('trust: the register lineage does not end at the current community anchor');
|
|
148
|
+
return structuredClone(seg.slice(-LINEAGE_MAX));
|
|
149
|
+
}
|
|
150
|
+
/** Whether the anchor mapping can go out on `relId` toward `contactId` now. */
|
|
151
|
+
function issuable(state, contactId, relId, lagMatters) {
|
|
152
|
+
const contact = findContact(state, contactId);
|
|
153
|
+
const rel = Object.hasOwn(state.relationships, relId) ? state.relationships[relId] : undefined;
|
|
154
|
+
if (!contact || !rel || rel.chainedInto !== null || !contact.relIds.includes(rel.id))
|
|
155
|
+
return { ok: false, why: 'gone' };
|
|
156
|
+
if (entryOf(state, rel)?.id !== contact.id)
|
|
157
|
+
return { ok: false, why: 'gone' }; // 2146: it sends under the other entry
|
|
158
|
+
if (!classOnToward(contact, 'anchorMapping'))
|
|
159
|
+
return { ok: false, why: 'off' }; // 6410
|
|
160
|
+
const tuple = activeTuple(rel);
|
|
161
|
+
if (!tuple || tuple.deactivated)
|
|
162
|
+
return { ok: false, why: 'deactivated' }; // 6240
|
|
163
|
+
if (lagMatters && lags(rel, entryRelationships(state, contact)))
|
|
164
|
+
return { ok: false, why: 'lagging' }; // 6595
|
|
165
|
+
if (state.register.community === null)
|
|
166
|
+
return { ok: false, why: 'no-community' };
|
|
167
|
+
if (BigInt(tuple.mappingOut.rev) + 1n > ADMISSION_BOUND)
|
|
168
|
+
return { ok: false, why: 'scope-exhausted' }; // 2.1
|
|
169
|
+
return { ok: true, contact, rel, tuple };
|
|
170
|
+
}
|
|
171
|
+
/** Build and seal the current anchor-mapping@4 for one relationship. */
|
|
172
|
+
async function prepareIssue(ctx, contactId, relId, lagMatters) {
|
|
173
|
+
const st = ctx.snapshot;
|
|
174
|
+
const chk = issuable(st, contactId, relId, lagMatters);
|
|
175
|
+
if (!chk.ok)
|
|
176
|
+
return null;
|
|
177
|
+
const { contact, rel, tuple: t } = chk;
|
|
178
|
+
const S = ctx.crypto.subtle;
|
|
179
|
+
const com = communityOf(st);
|
|
180
|
+
const self = await ctx.keys.anchorOf(com.label);
|
|
181
|
+
const next = await ctx.keys.anchorOf(com.nextLabel);
|
|
182
|
+
const own = await ctx.keys.anchorOf(t.ownPairLabel);
|
|
183
|
+
// self-card@2 under the current generation, bound to the next (6.2)
|
|
184
|
+
const cardBody = { type: 'self-card@2', anchor: self.anchor, keyAgreement: self.keyAgreement, commit: await bindingOf(next.anchor, S) };
|
|
185
|
+
const card = { body: cardBody, proof: { proofValue: rawProofValue(await ctx.keys.sign(com.label, te.encode(jcs(cardBody)))) } };
|
|
186
|
+
const rev = inc(t.mappingOut.rev);
|
|
187
|
+
const body = {
|
|
188
|
+
type: 'anchor-mapping@4', pair: own.anchor, self: self.anchor, to: t.counterpartAnchor, card,
|
|
189
|
+
lineage: lineageSegment(st.register.lineage, rel.earliestAnchor, self.anchor), revision: rev, issuedAt: iso(ctx.when),
|
|
190
|
+
};
|
|
191
|
+
const theirX = xRawOfMk(t.counterpartKeyAgreement);
|
|
192
|
+
if (!theirX)
|
|
193
|
+
return null;
|
|
194
|
+
// mac1 under the tuple's own pair key, mac2 under the community key (6030)
|
|
195
|
+
const sh1 = await ctx.keys.agree(t.rkid, theirX, ctx.release);
|
|
196
|
+
const communityRelease = (rkid) => (rkid === self.keyAgreement ? { status: 'live', label: com.label } : { status: 'unknown' });
|
|
197
|
+
const sh2 = await ctx.keys.agree(self.keyAgreement, theirX, communityRelease);
|
|
198
|
+
if (sh1.status !== 'available' || sh2.status !== 'available')
|
|
199
|
+
return null;
|
|
200
|
+
const mapping = await assembleAnchorMapping(body, sh1.shared, sh2.shared, S);
|
|
201
|
+
const threadId = ctx.entropy.id('thread.id');
|
|
202
|
+
const doc = {
|
|
203
|
+
id: ctx.entropy.id('doc.id'), type: ANCHOR_MAPPING_TYPE, issuer: own.anchor, recipient: t.counterpartAnchor,
|
|
204
|
+
threadId, issuedAt: iso(ctx.when), payload: mapping,
|
|
205
|
+
};
|
|
206
|
+
const envelope = await seal(doc, t.counterpartKeyAgreement, { ephSeed: ctx.entropy.bytes('seal.eph', 32), nonce: ctx.entropy.bytes('seal.nonce', 12) }, S);
|
|
207
|
+
return {
|
|
208
|
+
relId: rel.id, tuple: t.ownPairLabel, baseRev: t.mappingOut.rev, rev, body, underAnchor: self.anchor,
|
|
209
|
+
outbound: {
|
|
210
|
+
digest: await digestDoc(doc, S), envelope, type: ANCHOR_MAPPING_TYPE, threadId,
|
|
211
|
+
recipient: { contactId: contact.id, rkid: t.rkid, keyAgreement: t.counterpartKeyAgreement },
|
|
212
|
+
},
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/** The issuance's commit: revision and body persisted in one step (6300),
|
|
216
|
+
* the earliest anchor (2285), every duty the delivery meets (6596); null
|
|
217
|
+
* where the scope moved since the preparation. */
|
|
218
|
+
function commitIssue(state, p) {
|
|
219
|
+
const rel = state.relationships[p.relId];
|
|
220
|
+
const i = rel.tuples.findIndex((t) => t.ownPairLabel === p.tuple);
|
|
221
|
+
if (i < 0 || rel.tuples[i].mappingOut.rev !== p.baseRev)
|
|
222
|
+
return null;
|
|
223
|
+
const tuples = rel.tuples.map((t, j) => (j === i ? { ...t, mappingOut: { rev: p.rev, body: p.body, underAnchor: p.underAnchor } } : t));
|
|
224
|
+
const duties = {};
|
|
225
|
+
for (const [id, d] of Object.entries(state.duties))
|
|
226
|
+
if (!owedOn(d, p.relId, 'anchorMapping'))
|
|
227
|
+
duties[id] = d;
|
|
228
|
+
return {
|
|
229
|
+
state: {
|
|
230
|
+
...state,
|
|
231
|
+
relationships: { ...state.relationships, [rel.id]: { ...rel, tuples, earliestAnchor: rel.earliestAnchor ?? p.underAnchor } },
|
|
232
|
+
duties,
|
|
233
|
+
},
|
|
234
|
+
outbound: [p.outbound],
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
// ── the entry commit (Section 2) ───────────────────────────────────────
|
|
238
|
+
/**
|
|
239
|
+
* After a verified mapping on `relId` (or on recovery): the entry commit
|
|
240
|
+
* where the entry has none (2060: next position, admitted, or pending at
|
|
241
|
+
* the bound, 2250) and the entry's anchor head (6560). The class duties
|
|
242
|
+
* that follow (a relationship that ceased to lag, 2850) are the caller's,
|
|
243
|
+
* by the follow rule (trust/duties.ts).
|
|
244
|
+
*/
|
|
245
|
+
function commitEntry(state, relId, bound) {
|
|
246
|
+
const rel = state.relationships[relId];
|
|
247
|
+
const c0 = entryOf(state, rel);
|
|
248
|
+
if (!c0)
|
|
249
|
+
throw new Error(`trust: relationship ${relId} maps to no entry`);
|
|
250
|
+
let c = { ...c0 };
|
|
251
|
+
let entryOrder = state.entryOrder;
|
|
252
|
+
let committed = null;
|
|
253
|
+
if (c.pos === null) {
|
|
254
|
+
const admitted = BigInt(Object.values(state.contacts).filter((x) => x.status === 'admitted').length);
|
|
255
|
+
c = { ...c, pos: entryOrder.nextPos, status: admitted < bound ? 'admitted' : 'pending' };
|
|
256
|
+
entryOrder = { ...entryOrder, nextPos: inc(entryOrder.nextPos) };
|
|
257
|
+
committed = c;
|
|
258
|
+
}
|
|
259
|
+
const s1 = { ...state, contacts: { ...state.contacts, [c.id]: c }, entryOrder };
|
|
260
|
+
c = settleHead(s1, c, rel.heldCounterpartHead);
|
|
261
|
+
return { state: { ...s1, contacts: { ...s1.contacts, [c.id]: c } }, committed, contact: c };
|
|
262
|
+
}
|
|
263
|
+
/** First, pending entries are admitted in position order while the set
|
|
264
|
+
* has room (2250): they hold earlier positions than any re-commit. Then
|
|
265
|
+
* (2240) every relationship holding a verified mapping on a tuple whose
|
|
266
|
+
* entry holds no position re-commits that entry, in the order of
|
|
267
|
+
* entryOrder.heads (2180), then by relationship id, at the next position,
|
|
268
|
+
* admitted or pending by what room is left. */
|
|
269
|
+
export function recommitEntries(state, bound = ADMISSION_BOUND) {
|
|
270
|
+
const order = Object.keys(state.relationships).sort((a, b) => {
|
|
271
|
+
const ia = state.entryOrder.heads.indexOf(a), ib = state.entryOrder.heads.indexOf(b);
|
|
272
|
+
return ((ia < 0 ? Infinity : ia) - (ib < 0 ? Infinity : ib)) || (a < b ? -1 : a > b ? 1 : 0);
|
|
273
|
+
});
|
|
274
|
+
let cur = admitPending(state, bound);
|
|
275
|
+
for (const id of order) {
|
|
276
|
+
const rel = cur.relationships[id];
|
|
277
|
+
if (!rel.tuples.some((t) => t.mappingIn.body !== null) || entryOf(cur, rel)?.pos !== null)
|
|
278
|
+
continue;
|
|
279
|
+
cur = commitEntry(cur, id, bound).state;
|
|
280
|
+
}
|
|
281
|
+
const f = followClasses(state, cur);
|
|
282
|
+
return { state: f.state, ...(f.duties.length ? { duties: f.duties } : {}) };
|
|
283
|
+
}
|
|
284
|
+
// ── reception (6.3, Section 2) ─────────────────────────────────────────
|
|
285
|
+
const refuse = (reason, ref) => ({ ok: false, reason, ...(ref ? { ref } : {}) });
|
|
286
|
+
/** The arrival tuple, if it is the active head of a live relationship (6190, 6240). */
|
|
287
|
+
const arrivalTuple = (state, rkid) => {
|
|
288
|
+
const at = tupleByRkid(state, rkid);
|
|
289
|
+
if (!at || at.tuple.deactivated || at.rel.chainedInto !== null || at.rel.activeHead !== at.tuple.ownPairLabel)
|
|
290
|
+
return null;
|
|
291
|
+
return at;
|
|
292
|
+
};
|
|
293
|
+
function mappingTask(bound) {
|
|
294
|
+
return {
|
|
295
|
+
proof: 'absent',
|
|
296
|
+
// stage 7: the payload schema, calendar-valid issuedAt (6070)
|
|
297
|
+
decode(payload) {
|
|
298
|
+
if (!mappingSchemaOk(payload) || !calOK(payload.body.issuedAt))
|
|
299
|
+
return refuse('malformed', 'RLTP-TRU-6070');
|
|
300
|
+
return payload;
|
|
301
|
+
},
|
|
302
|
+
// stage 8: conditions 1–7 against the arrival tuple and the bindings
|
|
303
|
+
// its relationship holds (6118), then the bindings the mapping states
|
|
304
|
+
async prepare(ctx, doc, arrival) {
|
|
305
|
+
if (doc.ceremony?.enactment !== undefined)
|
|
306
|
+
return refuse('validation-failed');
|
|
307
|
+
const at = arrivalTuple(ctx.snapshot, arrival.rkid);
|
|
308
|
+
if (!at)
|
|
309
|
+
return refuse('validation-failed', 'RLTP-TRU-6240');
|
|
310
|
+
const S = ctx.crypto.subtle;
|
|
311
|
+
const own = await ctx.keys.anchorOf(at.tuple.ownPairLabel);
|
|
312
|
+
// 6118 against every binding held: an anchor the chain holds two
|
|
313
|
+
// bindings for (6610) admits no card and no successor, since none
|
|
314
|
+
// can meet both
|
|
315
|
+
const b0 = doc.payload.body;
|
|
316
|
+
const conflicted = new Set(Object.entries(at.rel.bindings).filter(([, v]) => v.length > 1).map(([k]) => k));
|
|
317
|
+
if (conflicted.has(b0.card.body.anchor) || b0.lineage.some((r) => conflicted.has(r.body.prev)))
|
|
318
|
+
return refuse('validation-failed', 'RLTP-TRU-6118');
|
|
319
|
+
let infra = null;
|
|
320
|
+
const v = await verifyAnchorMappingWith(doc.payload, {
|
|
321
|
+
ownAnchor: own.anchor, pair: at.tuple.counterpartAnchor, pairKeyAgreement: at.tuple.counterpartKeyAgreement,
|
|
322
|
+
held: new Map(Object.entries(at.rel.bindings).filter(([, x]) => x.length === 1).map(([k, x]) => [k, x[0]])),
|
|
323
|
+
agree: async (theirX) => {
|
|
324
|
+
try {
|
|
325
|
+
const a = await ctx.keys.agree(at.tuple.rkid, theirX, ctx.release);
|
|
326
|
+
return a.status === 'available' ? a.shared : null;
|
|
327
|
+
}
|
|
328
|
+
catch (e) {
|
|
329
|
+
infra = e;
|
|
330
|
+
throw e;
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
}, S);
|
|
334
|
+
if (infra !== null)
|
|
335
|
+
throw infra; // a provider failure is no verdict on the mapping
|
|
336
|
+
if (!v.ok)
|
|
337
|
+
return refuse('validation-failed', v.rule ?? MAPPING_CONDITION_RULE[v.step]);
|
|
338
|
+
// the anchors this mapping makes held (self, both ends of its links),
|
|
339
|
+
// blinded under every accepted star for the re-match of 6160
|
|
340
|
+
const anchors = [...new Set([doc.payload.body.self, ...lineageLinks(doc.payload.body.lineage).flat()])];
|
|
341
|
+
return { bindings: Object.fromEntries(await mappingBindings(doc.payload.body, S)), stars: await starBlinds(ctx, anchors) };
|
|
342
|
+
},
|
|
343
|
+
// stage 9: condition 8 (6150–6170) and the transition
|
|
344
|
+
apply(state, prepared, doc, arrival) {
|
|
345
|
+
const at = arrivalTuple(state, arrival.rkid);
|
|
346
|
+
if (!at)
|
|
347
|
+
return refuse('validation-failed', 'RLTP-TRU-6240');
|
|
348
|
+
const { rel, tuple: t } = at;
|
|
349
|
+
const b = doc.payload.body;
|
|
350
|
+
const rev = BigInt(b.revision);
|
|
351
|
+
const cur = BigInt(t.mappingIn.rev);
|
|
352
|
+
// the generic revision rule within the scope (pair, to) = this tuple (6270, 6280)
|
|
353
|
+
if (rev === cur) {
|
|
354
|
+
if (t.mappingIn.body === null || jcs(b) !== jcs(t.mappingIn.body))
|
|
355
|
+
return refuse('validation-failed', 'RLTP-TRU-6270');
|
|
356
|
+
// a repeat: ignored, except an entry lost with state loss re-commits (2240)
|
|
357
|
+
return entryOf(state, rel)?.pos === null ? { state: commitEntry(state, rel.id, bound).state } : { state: state };
|
|
358
|
+
}
|
|
359
|
+
if (rev < cur)
|
|
360
|
+
return refuse('validation-failed', 'RLTP-TRU-6270');
|
|
361
|
+
// condition 8 against the self this relationship holds (6150), never the entry's head
|
|
362
|
+
if (classifyMapping(rel.heldCounterpartHead ?? undefined, b) === 'rejected')
|
|
363
|
+
return refuse('validation-failed', 'RLTP-TRU-6170');
|
|
364
|
+
const tuples = rel.tuples.map((x) => (x.ownPairLabel === t.ownPairLabel
|
|
365
|
+
? { ...x, mappingIn: { rev: b.revision, body: b, self: b.self, card: b.card } }
|
|
366
|
+
: x));
|
|
367
|
+
// links and bindings accumulate at chain level (6610)
|
|
368
|
+
const links = [...rel.links];
|
|
369
|
+
for (const [prev, next] of lineageLinks(b.lineage))
|
|
370
|
+
if (!links.some((l) => l.prev === prev && l.next === next))
|
|
371
|
+
links.push({ prev, next });
|
|
372
|
+
const nextRel = {
|
|
373
|
+
...rel, tuples, links, bindings: unionBindings(rel.bindings, Object.fromEntries(Object.entries(prepared.value.bindings).map(([k, x]) => [k, [x]]))), heldCounterpartHead: b.self,
|
|
374
|
+
// 2190: the verification commit's place in the one total order
|
|
375
|
+
firstVerified: rel.firstVerified ?? inc(state.commitSeq),
|
|
376
|
+
};
|
|
377
|
+
let mid = { ...state, relationships: { ...state.relationships, [rel.id]: nextRel } };
|
|
378
|
+
const events = [];
|
|
379
|
+
// 2080: a manual merge the first mapping contradicts splits, atomically
|
|
380
|
+
const split = splitIfUnlinked(state, mid, rel.id);
|
|
381
|
+
if (split) {
|
|
382
|
+
mid = split.state;
|
|
383
|
+
events.push(split.event);
|
|
384
|
+
}
|
|
385
|
+
const e = commitEntry(mid, rel.id, bound);
|
|
386
|
+
if (e.committed)
|
|
387
|
+
events.push({ type: 'trust.received', contactId: e.contact.id });
|
|
388
|
+
events.push({ type: 'contact.verified', contactId: e.contact.id, relationshipId: rel.id });
|
|
389
|
+
// 6160: stars already accepted are tested against the anchors now held
|
|
390
|
+
// the net (6520–6605) and 2145: a merge this verification admits runs
|
|
391
|
+
// after its commit, as a persisted continuation (2230)
|
|
392
|
+
const m = mergeDutiesAfter(state, rematchStars(e.state, e.contact.id, prepared.value.stars), rel.id, { net: true });
|
|
393
|
+
// the class duties that follow (2850: a relationship that ceased to
|
|
394
|
+
// lag, a split's drop of 2115) — trust/duties.ts
|
|
395
|
+
const f = followClasses(state, m.state, m.duties);
|
|
396
|
+
const after = f.state;
|
|
397
|
+
const duties = f.duties;
|
|
398
|
+
return { state: after, events, ...(duties.length ? { duties } : {}), ...(split?.renames.length ? { renames: split.renames } : {}) };
|
|
399
|
+
},
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
// ── settings (Section 2a) ──────────────────────────────────────────────
|
|
403
|
+
const paused = (c) => c.settings.star === 'off' && c.settings.groupStar === 'off';
|
|
404
|
+
/** One change of a contact's settings: 2840 and 5450 follow from it, the
|
|
405
|
+
* class duties of 2850 and 6410 by the follow rule (trust/duties.ts), the
|
|
406
|
+
* events. */
|
|
407
|
+
function changeSettings(state, c, settings, groupSettings) {
|
|
408
|
+
// 5450: turning the sealed group star off resets every per-group on to
|
|
409
|
+
// default — on the transition only, so a group set on afterwards stays (2830)
|
|
410
|
+
let groups = groupSettings;
|
|
411
|
+
if (c.settings.sealedGroupStar !== 'off' && settings.sealedGroupStar === 'off')
|
|
412
|
+
groups = Object.fromEntries(Object.entries(groups).filter(([, s]) => s !== 'on'));
|
|
413
|
+
const next = { ...c, settings, groupSettings: groups };
|
|
414
|
+
const events = [];
|
|
415
|
+
if (!classOnToward(c, 'anchorMapping') && classOnToward(next, 'anchorMapping'))
|
|
416
|
+
events.push({ type: 'trust.given', contactId: next.id });
|
|
417
|
+
if (paused(c) !== paused(next))
|
|
418
|
+
events.push({ type: 'trust.paused', contactId: next.id, paused: paused(next) });
|
|
419
|
+
const f = followClasses(state, { ...state, contacts: { ...state.contacts, [c.id]: next } });
|
|
420
|
+
return { state: f.state, events, ...(f.duties.length ? { duties: f.duties } : {}) };
|
|
421
|
+
}
|
|
422
|
+
const needString = (x, what) => {
|
|
423
|
+
if (typeof x !== 'string' || x === '')
|
|
424
|
+
throw new TypeError(`${what} must be a non-empty string`);
|
|
425
|
+
return x;
|
|
426
|
+
};
|
|
427
|
+
const needSetting = (x) => {
|
|
428
|
+
if (!SETTINGS.includes(x))
|
|
429
|
+
throw new TypeError(`setting must be default, on or off, got ${String(x)}`);
|
|
430
|
+
return x;
|
|
431
|
+
};
|
|
432
|
+
const needInput = (x, name) => {
|
|
433
|
+
if (x === null || typeof x !== 'object' || Array.isArray(x))
|
|
434
|
+
throw new TypeError(`${name} needs an input object`);
|
|
435
|
+
return x;
|
|
436
|
+
};
|
|
437
|
+
/** A class setting through the one transition; `on` of the anchor mapping
|
|
438
|
+
* needs a community anchor to issue under. */
|
|
439
|
+
function setClass(state, contactId, cls, setting) {
|
|
440
|
+
const c = findContact(state, contactId);
|
|
441
|
+
if (!c)
|
|
442
|
+
return { ok: false, code: 'unknown-contact' };
|
|
443
|
+
const settings = { ...c.settings, [cls]: setting };
|
|
444
|
+
if (cls === 'anchorMapping' && setting === 'on' && state.register.community === null)
|
|
445
|
+
return { ok: false, code: 'no-community' };
|
|
446
|
+
return changeSettings(state, c, settings, c.groupSettings);
|
|
447
|
+
}
|
|
448
|
+
const commands = {
|
|
449
|
+
// the Trust button: on (2770), or withdrawn (2840)
|
|
450
|
+
[TRUST_COMMANDS.set]: {
|
|
451
|
+
human: true,
|
|
452
|
+
apply(state, _p, input) {
|
|
453
|
+
const i = needInput(input, TRUST_COMMANDS.set);
|
|
454
|
+
const contactId = needString(i.contactId, 'contactId');
|
|
455
|
+
if (typeof i.on !== 'boolean')
|
|
456
|
+
throw new TypeError('trust.set: on must be a boolean');
|
|
457
|
+
const c = findContact(state, contactId);
|
|
458
|
+
if (!c)
|
|
459
|
+
return { ok: false, code: 'unknown-contact' };
|
|
460
|
+
if (i.on && c.settings.anchorMapping === 'on')
|
|
461
|
+
return { ok: false, code: 'already-given' };
|
|
462
|
+
return setClass(state, contactId, 'anchorMapping', i.on ? 'on' : 'off');
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
[TRUST_COMMANDS.setClass]: {
|
|
466
|
+
human: true,
|
|
467
|
+
apply(state, _p, input) {
|
|
468
|
+
const i = needInput(input, TRUST_COMMANDS.setClass);
|
|
469
|
+
if (!CLASSES.includes(i.cls))
|
|
470
|
+
throw new TypeError(`trust.setClass: unknown class ${String(i.cls)}`);
|
|
471
|
+
return setClass(state, needString(i.contactId, 'contactId'), i.cls, needSetting(i.setting));
|
|
472
|
+
},
|
|
473
|
+
},
|
|
474
|
+
[TRUST_COMMANDS.setGroupClass]: {
|
|
475
|
+
human: true,
|
|
476
|
+
apply(state, _p, input) {
|
|
477
|
+
const i = needInput(input, TRUST_COMMANDS.setGroupClass);
|
|
478
|
+
const groupId = needString(i.groupId, 'groupId');
|
|
479
|
+
const setting = needSetting(i.setting);
|
|
480
|
+
const c = findContact(state, needString(i.contactId, 'contactId'));
|
|
481
|
+
if (!c)
|
|
482
|
+
return { ok: false, code: 'unknown-contact' };
|
|
483
|
+
const groups = { ...c.groupSettings };
|
|
484
|
+
if (setting === 'default')
|
|
485
|
+
delete groups[groupId];
|
|
486
|
+
else
|
|
487
|
+
groups[groupId] = setting;
|
|
488
|
+
return changeSettings(state, c, c.settings, groups);
|
|
489
|
+
},
|
|
490
|
+
},
|
|
491
|
+
// the pause of 5.4: star and blinded group star off (2780)
|
|
492
|
+
[TRUST_COMMANDS.pause]: {
|
|
493
|
+
human: true,
|
|
494
|
+
apply(state, _p, input) {
|
|
495
|
+
const i = needInput(input, TRUST_COMMANDS.pause);
|
|
496
|
+
if (typeof i.paused !== 'boolean')
|
|
497
|
+
throw new TypeError('trust.pause: paused must be a boolean');
|
|
498
|
+
const c = findContact(state, needString(i.contactId, 'contactId'));
|
|
499
|
+
if (!c)
|
|
500
|
+
return { ok: false, code: 'unknown-contact' };
|
|
501
|
+
const s = i.paused ? 'off' : 'default';
|
|
502
|
+
return changeSettings(state, c, { ...c.settings, star: s, groupStar: s }, c.groupSettings);
|
|
503
|
+
},
|
|
504
|
+
},
|
|
505
|
+
};
|
|
506
|
+
// ── duties ─────────────────────────────────────────────────────────────
|
|
507
|
+
/** What a blocked issuance means for a duty: fulfilled without sending
|
|
508
|
+
* (the class is off, the relationship gone or lagging), or kept. */
|
|
509
|
+
const blocked = (state, why) => why === 'no-community' ? { ok: false, code: 'no-community' }
|
|
510
|
+
: why === 'scope-exhausted' ? { ok: false, code: 'scope-exhausted', ref: 'RLTP-TRU-6310' }
|
|
511
|
+
: { state: state };
|
|
512
|
+
/** Issue the anchor mapping on a duty's relationship: class-on/1 of the
|
|
513
|
+
* anchor mapping (through classOnHandler) and reissue-mapping/1. */
|
|
514
|
+
const issueDuty = (lagMatters) => ({
|
|
515
|
+
v: 1,
|
|
516
|
+
prepare: (ctx, duty) => {
|
|
517
|
+
const d = duty;
|
|
518
|
+
return prepareIssue(ctx, d.contactId, d.relId, lagMatters);
|
|
519
|
+
},
|
|
520
|
+
apply(state, duty, ctx) {
|
|
521
|
+
const d = duty;
|
|
522
|
+
const chk = issuable(state, d.contactId, d.relId, lagMatters);
|
|
523
|
+
if (!chk.ok)
|
|
524
|
+
return blocked(state, chk.why);
|
|
525
|
+
const p = ctx.prepared.value;
|
|
526
|
+
if (p === null)
|
|
527
|
+
return { ok: false, code: 'no-own-channel', ref: 'RLTP-TRU-6140' }; // no usable key agreement
|
|
528
|
+
return commitIssue(state, p) ?? 'not-due';
|
|
529
|
+
},
|
|
530
|
+
});
|
|
531
|
+
/** 6596: an owed re-issue waits until its relationship presents the
|
|
532
|
+
* awaited head H as its anchor head, or a later anchor whose links carry
|
|
533
|
+
* H as a prev. A relationship gone releases the duty, which then drops. */
|
|
534
|
+
const reissueAwaits = (state, duty) => {
|
|
535
|
+
const d = duty;
|
|
536
|
+
if (!Object.hasOwn(state.relationships, d.relId))
|
|
537
|
+
return true;
|
|
538
|
+
const rel = state.relationships[d.relId];
|
|
539
|
+
return d.awaitHead === null || rel.heldCounterpartHead === d.awaitHead || rel.links.some((l) => l.prev === d.awaitHead);
|
|
540
|
+
};
|
|
541
|
+
/** The trust module createHolder builds in (see the header). */
|
|
542
|
+
export function trustModule(opts = {}) {
|
|
543
|
+
const bound = opts.admissionBound ?? ADMISSION_BOUND;
|
|
544
|
+
if (typeof bound !== 'bigint' || bound < 0n || bound > ADMISSION_BOUND)
|
|
545
|
+
throw new RangeError('admissionBound must be a bigint within [0, 10^18 − 1]');
|
|
546
|
+
const limit = opts.assemblyLimit;
|
|
547
|
+
if (limit !== undefined && (!Number.isSafeInteger(limit) || limit < 1))
|
|
548
|
+
throw new RangeError('assemblyLimit must be a positive integer');
|
|
549
|
+
const cont = continuityParts(bound, limit ?? ASSEMBLY_LIMIT.probe);
|
|
550
|
+
return Object.freeze({
|
|
551
|
+
name: 'trust',
|
|
552
|
+
commands: Object.freeze({
|
|
553
|
+
...commands,
|
|
554
|
+
...entryCommands(bound),
|
|
555
|
+
...profileParts.commands,
|
|
556
|
+
[TRUST_COMMANDS.recover]: { human: false, apply: (state) => recommitEntries(state, bound) },
|
|
557
|
+
}),
|
|
558
|
+
tasks: Object.freeze({
|
|
559
|
+
[ANCHOR_MAPPING_TYPE]: mappingTask(bound),
|
|
560
|
+
[STAR_TYPE]: taskOf(STAR, limit ?? ASSEMBLY_LIMIT.star),
|
|
561
|
+
[GROUP_STAR_TYPE]: taskOf(GROUP_STAR, limit ?? ASSEMBLY_LIMIT.groupStar),
|
|
562
|
+
...profileParts.tasks,
|
|
563
|
+
...cont.tasks,
|
|
564
|
+
}),
|
|
565
|
+
duties: Object.freeze({
|
|
566
|
+
// 2850: per class — the anchor mapping and the profile issue, the
|
|
567
|
+
// reconciled classes go by their sweep (trust/duties.ts)
|
|
568
|
+
'class-on': classOnHandler({ anchorMapping: issueDuty(true), profile: profileIssueDuty }),
|
|
569
|
+
'reissue-mapping': { ...issueDuty(false), awaitHolds: reissueAwaits },
|
|
570
|
+
profile: profileIssueDuty,
|
|
571
|
+
merge: mergeDuty(bound),
|
|
572
|
+
...cont.duties,
|
|
573
|
+
}),
|
|
574
|
+
// 5.4: the star first, then the group star, each its own scope (5730)
|
|
575
|
+
sweeps: Object.freeze({ 'trust.star': sweepOf(STAR), 'trust.groupStar': sweepOf(GROUP_STAR) }),
|
|
576
|
+
acks: Object.freeze({ [STAR_TYPE]: ackOf(STAR), [GROUP_STAR_TYPE]: ackOf(GROUP_STAR), ...cont.acks }),
|
|
577
|
+
failures: Object.freeze({ ...cont.failures }),
|
|
578
|
+
ownRetries: cont.ownRetries,
|
|
579
|
+
keepKeys: cont.keepKeys,
|
|
580
|
+
// in-flight deliveries whose outbox items are all gone leave on tick
|
|
581
|
+
retain: (state) => retainInflight(state),
|
|
582
|
+
});
|
|
583
|
+
}
|
|
584
|
+
//# sourceMappingURL=mapping.js.map
|