@dxos/credentials 0.5.1-next.638da00 → 0.5.1-next.65aaa36
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/dist/lib/browser/index.mjs +167 -740
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/node/index.cjs +144 -713
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/types/src/credentials/credential-factory.d.ts +1 -3
- package/dist/types/src/credentials/credential-factory.d.ts.map +1 -1
- package/dist/types/src/credentials/credential-generator.d.ts +2 -12
- package/dist/types/src/credentials/credential-generator.d.ts.map +1 -1
- package/dist/types/src/credentials/verifier.d.ts +1 -1
- package/dist/types/src/credentials/verifier.d.ts.map +1 -1
- package/dist/types/src/state-machine/member-state-machine.d.ts +12 -17
- package/dist/types/src/state-machine/member-state-machine.d.ts.map +1 -1
- package/dist/types/src/state-machine/space-state-machine.d.ts +3 -3
- package/dist/types/src/state-machine/space-state-machine.d.ts.map +1 -1
- package/package.json +16 -16
- package/src/credentials/credential-factory.ts +1 -9
- package/src/credentials/credential-generator.ts +2 -15
- package/src/credentials/verifier.test.ts +0 -25
- package/src/credentials/verifier.ts +1 -5
- package/src/state-machine/invitation-state-machine.test.ts +1 -1
- package/src/state-machine/member-state-machine.ts +53 -111
- package/src/state-machine/space-state-machine.test.ts +16 -20
- package/src/state-machine/space-state-machine.ts +33 -12
- package/dist/types/src/graph/credential-graph.d.ts +0 -152
- package/dist/types/src/graph/credential-graph.d.ts.map +0 -1
- package/dist/types/src/state-machine/member-state-machine.test.d.ts +0 -2
- package/dist/types/src/state-machine/member-state-machine.test.d.ts.map +0 -1
- package/src/graph/credential-graph.ts +0 -557
- package/src/state-machine/member-state-machine.test.ts +0 -580
|
@@ -5,23 +5,16 @@
|
|
|
5
5
|
import { invariant } from '@dxos/invariant';
|
|
6
6
|
import { PublicKey } from '@dxos/keys';
|
|
7
7
|
import { log } from '@dxos/log';
|
|
8
|
-
import { type Credential, SpaceMember, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
-
import { ComplexMap } from '@dxos/util';
|
|
8
|
+
import { type Credential, type SpaceMember, type ProfileDocument } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
9
|
+
import { type AsyncCallback, Callback, ComplexMap } from '@dxos/util';
|
|
10
10
|
|
|
11
11
|
import { getCredentialAssertion } from '../credentials';
|
|
12
|
-
import {
|
|
13
|
-
type ChainVertex,
|
|
14
|
-
CredentialGraph,
|
|
15
|
-
type CredentialGraphStateHandler,
|
|
16
|
-
type PathState,
|
|
17
|
-
type StateScope,
|
|
18
|
-
} from '../graph/credential-graph';
|
|
19
12
|
|
|
20
13
|
export interface MemberInfo {
|
|
21
14
|
key: PublicKey;
|
|
22
|
-
role: SpaceMember.Role;
|
|
23
15
|
credential: Credential;
|
|
24
16
|
assertion: SpaceMember;
|
|
17
|
+
removed: boolean;
|
|
25
18
|
profile?: ProfileDocument;
|
|
26
19
|
}
|
|
27
20
|
|
|
@@ -29,29 +22,28 @@ export interface MemberInfo {
|
|
|
29
22
|
* Tracks the list of members (with roles) for the space.
|
|
30
23
|
* Provides a list of admitted feeds.
|
|
31
24
|
*/
|
|
32
|
-
export class MemberStateMachine
|
|
33
|
-
private
|
|
34
|
-
private _memberProfiles = new ComplexMap<PublicKey, ProfileDocument | undefined>(PublicKey.hash);
|
|
35
|
-
private _hashgraph = new CredentialGraph<SpaceMember, MemberInfo>(this);
|
|
25
|
+
export class MemberStateMachine {
|
|
26
|
+
private _creator: MemberInfo | undefined;
|
|
36
27
|
|
|
37
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Member IDENTITY key => info
|
|
30
|
+
*/
|
|
31
|
+
private _members = new ComplexMap<PublicKey, MemberInfo>(PublicKey.hash);
|
|
32
|
+
|
|
33
|
+
readonly onMemberAdmitted = new Callback<AsyncCallback<MemberInfo>>();
|
|
38
34
|
|
|
39
35
|
constructor(private readonly _spaceKey: PublicKey) {}
|
|
40
36
|
|
|
41
37
|
get creator(): MemberInfo | undefined {
|
|
42
|
-
return this.
|
|
38
|
+
return this._creator;
|
|
43
39
|
}
|
|
44
40
|
|
|
45
41
|
get members(): ReadonlyMap<PublicKey, MemberInfo> {
|
|
46
|
-
return this.
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
get membershipChainHeads(): PublicKey[] {
|
|
50
|
-
return this._hashgraph.getLeafIds();
|
|
42
|
+
return this._members;
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
getRole(member: PublicKey): SpaceMember.Role {
|
|
54
|
-
return this.
|
|
45
|
+
getRole(member: PublicKey): SpaceMember.Role | undefined {
|
|
46
|
+
return this._members.get(member)?.assertion.role;
|
|
55
47
|
}
|
|
56
48
|
|
|
57
49
|
/**
|
|
@@ -64,109 +56,59 @@ export class MemberStateMachine implements CredentialGraphStateHandler<SpaceMemb
|
|
|
64
56
|
switch (assertion['@type']) {
|
|
65
57
|
case 'dxos.halo.credentials.SpaceMember': {
|
|
66
58
|
invariant(assertion.spaceKey.equals(this._spaceKey));
|
|
67
|
-
|
|
68
|
-
|
|
59
|
+
|
|
60
|
+
// Ignore duplicate admissions.
|
|
61
|
+
if (this._members.has(credential.subject.id)) {
|
|
62
|
+
return;
|
|
69
63
|
}
|
|
70
|
-
|
|
71
|
-
|
|
64
|
+
|
|
65
|
+
const info: MemberInfo = {
|
|
66
|
+
key: credential.subject.id,
|
|
67
|
+
credential,
|
|
68
|
+
assertion,
|
|
69
|
+
removed: false,
|
|
70
|
+
profile: assertion.profile,
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// NOTE: Assumes the first member processed is the creator.
|
|
74
|
+
if (!this._creator) {
|
|
75
|
+
this._creator = info;
|
|
72
76
|
}
|
|
73
|
-
|
|
77
|
+
|
|
78
|
+
this._members.set(credential.subject.id, info);
|
|
79
|
+
log('member added', {
|
|
80
|
+
member: credential.subject.id,
|
|
81
|
+
space: this._spaceKey,
|
|
82
|
+
role: assertion.role,
|
|
83
|
+
profile: assertion.profile,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
await this.onMemberAdmitted.callIfSet(info);
|
|
74
87
|
break;
|
|
75
88
|
}
|
|
89
|
+
|
|
76
90
|
case 'dxos.halo.credentials.MemberProfile': {
|
|
77
|
-
const member = this.
|
|
78
|
-
if (member) {
|
|
79
|
-
member.profile = assertion.profile;
|
|
80
|
-
} else {
|
|
91
|
+
const member = this._members.get(credential.subject.id);
|
|
92
|
+
if (!member) {
|
|
81
93
|
log.warn('Member not found', { id: credential.subject.id });
|
|
94
|
+
} else {
|
|
95
|
+
member.profile = assertion.profile;
|
|
82
96
|
}
|
|
83
|
-
this._memberProfiles.set(credential.subject.id, assertion.profile);
|
|
84
97
|
break;
|
|
85
98
|
}
|
|
99
|
+
|
|
86
100
|
default:
|
|
87
101
|
throw new Error('Invalid assertion type');
|
|
88
102
|
}
|
|
89
103
|
}
|
|
90
104
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
assertion,
|
|
98
|
-
profile: this._memberProfiles.get(memberKey),
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
public isUpdateAllowed(scope: StateScope<SpaceMember>, credential: Credential, assertion: SpaceMember) {
|
|
103
|
-
if (assertion.role === SpaceMember.Role.OWNER) {
|
|
104
|
-
return credential!.issuer.equals(this._spaceKey);
|
|
105
|
-
}
|
|
106
|
-
const issuer = credential.issuer;
|
|
107
|
-
const isChangingOwnRole = issuer.equals(credential.subject.id);
|
|
108
|
-
if (isChangingOwnRole) {
|
|
109
|
-
return false;
|
|
110
|
-
}
|
|
111
|
-
if (issuer.equals(assertion.spaceKey)) {
|
|
112
|
-
return true;
|
|
113
|
-
}
|
|
114
|
-
const issuerRole = this._getRole(scope, issuer);
|
|
115
|
-
return issuerRole === SpaceMember.Role.ADMIN || issuerRole === SpaceMember.Role.OWNER;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
public getConflictingPaths(
|
|
119
|
-
paths: PathState<SpaceMember>[],
|
|
120
|
-
update: ChainVertex<SpaceMember>,
|
|
121
|
-
): PathState<SpaceMember>[] {
|
|
122
|
-
// a member can't be an issuer in a concurrent branch if we decided to remove or revoke admin permissions during merge
|
|
123
|
-
if (update.assertion.role !== SpaceMember.Role.REMOVED && update.assertion.role !== SpaceMember.Role.EDITOR) {
|
|
124
|
-
return [];
|
|
125
|
-
}
|
|
126
|
-
const memberId = update.credential!.subject.id!;
|
|
127
|
-
return paths.filter((p) => p.forkIssuers.has(memberId));
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
public tryPickWinningUpdate(
|
|
131
|
-
scope1: StateScope<SpaceMember>,
|
|
132
|
-
update1: Credential,
|
|
133
|
-
scope2: StateScope<SpaceMember>,
|
|
134
|
-
update2: Credential,
|
|
135
|
-
): Credential | null {
|
|
136
|
-
const path1IssuerRole = this._getRole(scope1, update1.issuer);
|
|
137
|
-
const path2IssuerRole = this._getRole(scope2, update2.issuer);
|
|
138
|
-
if ((path2IssuerRole === SpaceMember.Role.OWNER) !== (path1IssuerRole === SpaceMember.Role.OWNER)) {
|
|
139
|
-
log('owner decision used to break the tie');
|
|
140
|
-
return path1IssuerRole === SpaceMember.Role.OWNER ? update1 : update2;
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
public toLogString(assertion: SpaceMember | undefined): string {
|
|
146
|
-
const role = assertion?.role ?? SpaceMember.Role.REMOVED;
|
|
147
|
-
return Object.entries(SpaceMember.Role).find(([_, value]) => value === role)![0];
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
public hasStateChanged(s1?: MemberInfo, s2?: MemberInfo): boolean {
|
|
151
|
-
return s1?.role !== s2?.role;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
private _getRole(scope: StateScope<SpaceMember>, memberId: PublicKey): SpaceMember.Role {
|
|
155
|
-
if (this._ownerKey?.equals(memberId)) {
|
|
156
|
-
return SpaceMember.Role.OWNER;
|
|
157
|
-
}
|
|
158
|
-
const realRole = scope.state.get(memberId)?.assertion?.role ?? SpaceMember.Role.REMOVED;
|
|
159
|
-
if (scope.stateOverrides != null) {
|
|
160
|
-
const override = scope.stateOverrides.get(memberId);
|
|
161
|
-
if (override != null) {
|
|
162
|
-
log('member role overridden in path', () => ({
|
|
163
|
-
headId: scope.head?.id,
|
|
164
|
-
roleOverride: this.toLogString(override.assertion),
|
|
165
|
-
realRole: this.toLogString(scope.state.get(memberId)?.assertion),
|
|
166
|
-
}));
|
|
167
|
-
return override.assertion.role;
|
|
105
|
+
async onRevoked(revoked: Credential, revocation: Credential) {
|
|
106
|
+
invariant(revoked.id);
|
|
107
|
+
for (const member of this._members.values()) {
|
|
108
|
+
if (member.credential.id?.equals(revoked.id)) {
|
|
109
|
+
member.removed = true;
|
|
110
|
+
break;
|
|
168
111
|
}
|
|
169
112
|
}
|
|
170
|
-
return realRole;
|
|
171
113
|
}
|
|
172
114
|
}
|
|
@@ -5,7 +5,6 @@
|
|
|
5
5
|
import expect from 'expect';
|
|
6
6
|
|
|
7
7
|
import { Keyring } from '@dxos/keyring';
|
|
8
|
-
import { type PublicKey } from '@dxos/keys';
|
|
9
8
|
import { AdmittedFeed, type Chain, SpaceMember } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
10
9
|
import { describe, test } from '@dxos/test';
|
|
11
10
|
|
|
@@ -137,7 +136,7 @@ describe('SpaceStateMachine', () => {
|
|
|
137
136
|
),
|
|
138
137
|
).toEqual(true);
|
|
139
138
|
|
|
140
|
-
//
|
|
139
|
+
// Create the space member credential.
|
|
141
140
|
expect(
|
|
142
141
|
await spaceState.process(
|
|
143
142
|
await createCredential({
|
|
@@ -176,7 +175,7 @@ describe('SpaceStateMachine', () => {
|
|
|
176
175
|
assertion: {
|
|
177
176
|
'@type': 'dxos.halo.credentials.SpaceMember',
|
|
178
177
|
spaceKey: space,
|
|
179
|
-
role: SpaceMember.Role.
|
|
178
|
+
role: SpaceMember.Role.MEMBER,
|
|
180
179
|
genesisFeedKey: feed,
|
|
181
180
|
},
|
|
182
181
|
signer: keyring,
|
|
@@ -188,25 +187,22 @@ describe('SpaceStateMachine', () => {
|
|
|
188
187
|
).toEqual(true);
|
|
189
188
|
|
|
190
189
|
expect(spaceState.genesisCredential).toBeDefined();
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
{
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
spaceKey: space,
|
|
198
|
-
role: SpaceMember.Role.ADMIN,
|
|
199
|
-
},
|
|
190
|
+
expect(Array.from(spaceState.members.values())).toMatchObject([
|
|
191
|
+
{
|
|
192
|
+
key: identity,
|
|
193
|
+
assertion: {
|
|
194
|
+
spaceKey: space,
|
|
195
|
+
role: SpaceMember.Role.ADMIN,
|
|
200
196
|
},
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
key: identity2,
|
|
200
|
+
assertion: {
|
|
201
|
+
spaceKey: space,
|
|
202
|
+
role: SpaceMember.Role.MEMBER,
|
|
207
203
|
},
|
|
208
|
-
|
|
209
|
-
);
|
|
204
|
+
},
|
|
205
|
+
]);
|
|
210
206
|
expect(Array.from(spaceState.feeds.values())).toMatchObject([]);
|
|
211
207
|
expect(spaceState.credentials).toHaveLength(3);
|
|
212
208
|
});
|
|
@@ -19,7 +19,6 @@ import { type CredentialProcessor } from '../processor/credential-processor';
|
|
|
19
19
|
|
|
20
20
|
export interface SpaceState {
|
|
21
21
|
readonly members: ReadonlyMap<PublicKey, MemberInfo>;
|
|
22
|
-
readonly membershipChainHeads: PublicKey[];
|
|
23
22
|
readonly feeds: ReadonlyMap<PublicKey, FeedInfo>;
|
|
24
23
|
readonly credentials: Credential[];
|
|
25
24
|
readonly genesisCredential: Credential | undefined;
|
|
@@ -43,6 +42,8 @@ export type CredentialEntry = {
|
|
|
43
42
|
revoked: boolean;
|
|
44
43
|
};
|
|
45
44
|
|
|
45
|
+
const REVOCABLE_CREDENTIALS = ['dxos.halo.credentials.SpaceMember'];
|
|
46
|
+
|
|
46
47
|
/**
|
|
47
48
|
* Validates and processes credentials for a single space.
|
|
48
49
|
* Keeps a list of members and feeds.
|
|
@@ -60,7 +61,7 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
60
61
|
private _credentialProcessors: CredentialConsumer<any>[] = [];
|
|
61
62
|
|
|
62
63
|
readonly onCredentialProcessed = new Callback<AsyncCallback<Credential>>();
|
|
63
|
-
readonly
|
|
64
|
+
readonly onMemberAdmitted = this._members.onMemberAdmitted;
|
|
64
65
|
readonly onFeedAdmitted = this._feeds.onFeedAdmitted;
|
|
65
66
|
readonly onDelegatedInvitation = this._invitations.onDelegatedInvitation;
|
|
66
67
|
readonly onDelegatedInvitationRemoved = this._invitations.onDelegatedInvitationRemoved;
|
|
@@ -75,10 +76,6 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
75
76
|
return this._members.members;
|
|
76
77
|
}
|
|
77
78
|
|
|
78
|
-
get membershipChainHeads(): PublicKey[] {
|
|
79
|
-
return this._members.membershipChainHeads;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
79
|
get feeds(): ReadonlyMap<PublicKey, FeedInfo> {
|
|
83
80
|
return this._feeds.feeds;
|
|
84
81
|
}
|
|
@@ -156,6 +153,24 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
156
153
|
|
|
157
154
|
const assertion = getCredentialAssertion(credential);
|
|
158
155
|
switch (assertion['@type']) {
|
|
156
|
+
case 'dxos.halo.credentials.Revocation': {
|
|
157
|
+
if (!this._canRevokeCredentials(credential.issuer)) {
|
|
158
|
+
log.warn(`Space member is not authorized to invite new members: ${credential.issuer}`);
|
|
159
|
+
return false;
|
|
160
|
+
}
|
|
161
|
+
const toBeRevoked = this._credentialsById.get(credential.subject.id);
|
|
162
|
+
if (!toBeRevoked) {
|
|
163
|
+
log.warn(`Credential to revoke not found: ${credential.subject.id}`);
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
if (!this._credentialCanBeRevoked(toBeRevoked)) {
|
|
167
|
+
log.warn(`Credential cannot be revoked: ${credential.subject.id}`);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
toBeRevoked.revoked = true;
|
|
171
|
+
await this._members.onRevoked(toBeRevoked.credential, credential);
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
159
174
|
case 'dxos.halo.credentials.SpaceGenesis': {
|
|
160
175
|
if (this._genesisCredential) {
|
|
161
176
|
log.warn('Space already has a genesis credential.');
|
|
@@ -169,6 +184,7 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
169
184
|
log.warn('Space genesis credential must be issued to space.');
|
|
170
185
|
return false;
|
|
171
186
|
}
|
|
187
|
+
|
|
172
188
|
this._genesisCredential = credential;
|
|
173
189
|
break;
|
|
174
190
|
}
|
|
@@ -246,16 +262,21 @@ export class SpaceStateMachine implements SpaceState {
|
|
|
246
262
|
}
|
|
247
263
|
|
|
248
264
|
private _canInviteNewMembers(key: PublicKey): boolean {
|
|
249
|
-
return (
|
|
250
|
-
key.equals(this._spaceKey) ||
|
|
251
|
-
this._members.getRole(key) === SpaceMember.Role.ADMIN ||
|
|
252
|
-
this._members.getRole(key) === SpaceMember.Role.OWNER
|
|
253
|
-
);
|
|
265
|
+
return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember.Role.ADMIN;
|
|
254
266
|
}
|
|
255
267
|
|
|
256
268
|
private _canAdmitFeeds(key: PublicKey): boolean {
|
|
257
269
|
const role = this._members.getRole(key);
|
|
258
|
-
return role === SpaceMember.Role.
|
|
270
|
+
return role === SpaceMember.Role.MEMBER || role === SpaceMember.Role.ADMIN;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
private _canRevokeCredentials(key: PublicKey): boolean {
|
|
274
|
+
return key.equals(this._spaceKey) || this._members.getRole(key) === SpaceMember.Role.ADMIN;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private _credentialCanBeRevoked(entry: CredentialEntry) {
|
|
278
|
+
// TODO(dmaretskyi): Prohibit from removing space's creator member credential.
|
|
279
|
+
return REVOCABLE_CREDENTIALS.includes(entry.credential.subject.assertion['@type']);
|
|
259
280
|
}
|
|
260
281
|
}
|
|
261
282
|
|
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
import { PublicKey } from '@dxos/keys';
|
|
2
|
-
import { type Credential } from '@dxos/protocols/proto/dxos/halo/credentials';
|
|
3
|
-
import { type AsyncCallback, Callback, ComplexMap, ComplexSet } from '@dxos/util';
|
|
4
|
-
export declare class CredentialGraph<A, State> {
|
|
5
|
-
private readonly _stateHandler;
|
|
6
|
-
/**
|
|
7
|
-
* Local ids are used during traversals.
|
|
8
|
-
*/
|
|
9
|
-
private _vertexIdGenerator;
|
|
10
|
-
/**
|
|
11
|
-
* All credentials without parent references are connected to the root.
|
|
12
|
-
*/
|
|
13
|
-
private _root;
|
|
14
|
-
/**
|
|
15
|
-
* A credential which is not a parent of any other credential has the sentinel as a child.
|
|
16
|
-
* Sentinel is a virtual merge-point of all credentials.
|
|
17
|
-
*/
|
|
18
|
-
private _sentinel;
|
|
19
|
-
/**
|
|
20
|
-
* Vertex references are used for fast credential inserts into the graph.
|
|
21
|
-
*/
|
|
22
|
-
private _vertexByCredentialId;
|
|
23
|
-
/**
|
|
24
|
-
* The current state of the graph.
|
|
25
|
-
*/
|
|
26
|
-
private _subjectToVertex;
|
|
27
|
-
private _subjectToState;
|
|
28
|
-
onSubjectStateChanged: Callback<AsyncCallback<State[]>>;
|
|
29
|
-
constructor(_stateHandler: CredentialGraphStateHandler<A, State>);
|
|
30
|
-
getSubjectState(subjectId: PublicKey): State | undefined;
|
|
31
|
-
getState(): ReadonlyMap<PublicKey, State>;
|
|
32
|
-
getLeafIds(): PublicKey[];
|
|
33
|
-
getGlobalStateScope(): StateScope<A>;
|
|
34
|
-
addVertex(credential: Credential, assertion: A): Promise<void>;
|
|
35
|
-
private _removeSentinelConnection;
|
|
36
|
-
private _onVertexInserted;
|
|
37
|
-
/**
|
|
38
|
-
* DFS the graph from root to sentinel pausing on merge points (nodes with multiple parents).
|
|
39
|
-
* Continue after all paths leading to a merge point converge by merging their states.
|
|
40
|
-
* In case of a concurrent update paths are replayed taking into account the state set
|
|
41
|
-
* by the winning branch.
|
|
42
|
-
*/
|
|
43
|
-
private _recomputeState;
|
|
44
|
-
private _replayFailedPaths;
|
|
45
|
-
private _handleMergePoint;
|
|
46
|
-
private _updatePathState;
|
|
47
|
-
private _forkTraversal;
|
|
48
|
-
/**
|
|
49
|
-
* Updates the current graph state.
|
|
50
|
-
* @returns changed states.
|
|
51
|
-
*/
|
|
52
|
-
private _setCurrentState;
|
|
53
|
-
private _leastCommonAncestor;
|
|
54
|
-
/**
|
|
55
|
-
* We might be merging paths where some of them had fork points after the initial forking.
|
|
56
|
-
* We need all the paths to point to the least common fork point and contain all the changes
|
|
57
|
-
* that happened after it.
|
|
58
|
-
*/
|
|
59
|
-
private _moveUpToForkPoint;
|
|
60
|
-
private _mergePaths;
|
|
61
|
-
/**
|
|
62
|
-
* A candidate credential is preferred over the existing credential if:
|
|
63
|
-
* 1. It is the merge-point, because it's the last credential that was issued in awareness of all
|
|
64
|
-
* the previously existing ones.
|
|
65
|
-
* 2. A path where candidate was set contains existing credential in it, which means that the candidate
|
|
66
|
-
* was issued after the existing credential by a legitimate issuer.
|
|
67
|
-
* 3. A state-specific logic (_stateHandler) is able to justify using the candidate credential.
|
|
68
|
-
* 4. The path where candidate was set has more issuers than the existing path (longer branch).
|
|
69
|
-
* 5. The issuance time of the candidate is after the issuance time of the existing credential (LWW).
|
|
70
|
-
*/
|
|
71
|
-
private _shouldOverrideCredential;
|
|
72
|
-
private _createRootPath;
|
|
73
|
-
}
|
|
74
|
-
export interface StateScope<A> {
|
|
75
|
-
head?: {
|
|
76
|
-
id: number;
|
|
77
|
-
};
|
|
78
|
-
state: ReadonlyMap<PublicKey, ChainVertex<A>>;
|
|
79
|
-
stateOverrides?: ReadonlyMap<PublicKey, ChainVertex<A>>;
|
|
80
|
-
}
|
|
81
|
-
export interface CredentialGraphStateHandler<Assertion, State> {
|
|
82
|
-
hasStateChanged(s1?: State, s2?: State): boolean;
|
|
83
|
-
createState(credential: Credential, assertion: Assertion): State;
|
|
84
|
-
isUpdateAllowed: (scope: StateScope<Assertion>, update: Credential, assertion: Assertion) => boolean;
|
|
85
|
-
getConflictingPaths(paths: PathState<Assertion>[], update: ChainVertex<Assertion>): PathState<Assertion>[];
|
|
86
|
-
tryPickWinningUpdate(scope1: StateScope<Assertion>, update1: Credential, scope2: StateScope<Assertion>, update2: Credential): Credential | null;
|
|
87
|
-
toLogString(assertion: Assertion): string;
|
|
88
|
-
}
|
|
89
|
-
export interface PathState<A> {
|
|
90
|
-
/**
|
|
91
|
-
* The current vertex position in path, always advances.
|
|
92
|
-
*/
|
|
93
|
-
head: ChainVertex<A>;
|
|
94
|
-
/**
|
|
95
|
-
* Subject info local to the current path.
|
|
96
|
-
*/
|
|
97
|
-
state: ComplexMap<PublicKey, ChainVertex<A>>;
|
|
98
|
-
/**
|
|
99
|
-
* Used during path replay to throw away cascading concurrent modifications.
|
|
100
|
-
* Overrides pathState.
|
|
101
|
-
*/
|
|
102
|
-
stateOverrides?: ComplexMap<PublicKey, ChainVertex<A>>;
|
|
103
|
-
/**
|
|
104
|
-
* Used to faster search of conflicting branches. Is different from pathState.keys()
|
|
105
|
-
* because pathState is not reset on forks.
|
|
106
|
-
*/
|
|
107
|
-
forkChangedSubjects: ComplexSet<PublicKey>;
|
|
108
|
-
/**
|
|
109
|
-
* Used to find winning branches. A branch wins if it had more participants.
|
|
110
|
-
* Ties are broken using credential issuance date.
|
|
111
|
-
*/
|
|
112
|
-
forkIssuers: ComplexSet<PublicKey>;
|
|
113
|
-
/**
|
|
114
|
-
* All the credentials processed during this path traversal.
|
|
115
|
-
*/
|
|
116
|
-
credentials: ComplexSet<PublicKey>;
|
|
117
|
-
/**
|
|
118
|
-
* PathState where we had multiple children in the current vertex.
|
|
119
|
-
* Will be merged with child branches when they converge.
|
|
120
|
-
*/
|
|
121
|
-
forkPoint?: PathState<A>;
|
|
122
|
-
/**
|
|
123
|
-
* Used for a particular path replay with stateOverrides for conflict resolution
|
|
124
|
-
* forkVertexId is mapped to child vertex selection.
|
|
125
|
-
* Contains choices that lead to the current state. Value is an array when
|
|
126
|
-
* some branches converged before converging with the remaining branches.
|
|
127
|
-
*/
|
|
128
|
-
chosenPath: {
|
|
129
|
-
[forkVertexId: number]: ChainVertex<A>[];
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
export interface ChainVertex<Assertion> {
|
|
133
|
-
/**
|
|
134
|
-
* The field is missing on root and sentinel vertices. Assertion is not undefined to avoid
|
|
135
|
-
* always asserting two fields.
|
|
136
|
-
*/
|
|
137
|
-
credential?: Credential;
|
|
138
|
-
assertion: Assertion;
|
|
139
|
-
/**
|
|
140
|
-
* Local incrementing counter used to form paths. Is used only for causality resolution.
|
|
141
|
-
*/
|
|
142
|
-
id: number;
|
|
143
|
-
/**
|
|
144
|
-
* Parents references are used to handle divergent branch merge-points.
|
|
145
|
-
*/
|
|
146
|
-
parents: ChainVertex<Assertion>[];
|
|
147
|
-
/**
|
|
148
|
-
* Child references are traversed when computing the current state.
|
|
149
|
-
*/
|
|
150
|
-
children: ChainVertex<Assertion>[];
|
|
151
|
-
}
|
|
152
|
-
//# sourceMappingURL=credential-graph.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"credential-graph.d.ts","sourceRoot":"","sources":["../../../../src/graph/credential-graph.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"member-state-machine.test.d.ts","sourceRoot":"","sources":["../../../../src/state-machine/member-state-machine.test.ts"],"names":[],"mappings":""}
|