@ledgerhq/ledger-key-ring-protocol 0.5.1-nightly.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.
Files changed (180) hide show
  1. package/.eslintrc.js +33 -0
  2. package/.turbo/turbo-build.log +4 -0
  3. package/.unimportedrc.json +16 -0
  4. package/CHANGELOG.md +299 -0
  5. package/LICENSE.txt +21 -0
  6. package/README.md +3 -0
  7. package/jest.config.js +13 -0
  8. package/lib/HWDeviceProvider.d.ts +25 -0
  9. package/lib/HWDeviceProvider.d.ts.map +1 -0
  10. package/lib/HWDeviceProvider.js +88 -0
  11. package/lib/HWDeviceProvider.js.map +1 -0
  12. package/lib/api.d.ts +77 -0
  13. package/lib/api.d.ts.map +1 -0
  14. package/lib/api.js +150 -0
  15. package/lib/api.js.map +1 -0
  16. package/lib/auth.d.ts +3 -0
  17. package/lib/auth.d.ts.map +1 -0
  18. package/lib/auth.js +79 -0
  19. package/lib/auth.js.map +1 -0
  20. package/lib/errors.d.ts +40 -0
  21. package/lib/errors.d.ts.map +1 -0
  22. package/lib/errors.js +18 -0
  23. package/lib/errors.js.map +1 -0
  24. package/lib/index.d.ts +6 -0
  25. package/lib/index.d.ts.map +1 -0
  26. package/lib/index.js +17 -0
  27. package/lib/index.js.map +1 -0
  28. package/lib/mockSdk.d.ts +22 -0
  29. package/lib/mockSdk.d.ts.map +1 -0
  30. package/lib/mockSdk.js +208 -0
  31. package/lib/mockSdk.js.map +1 -0
  32. package/lib/qrcode/cipher.d.ts +12 -0
  33. package/lib/qrcode/cipher.d.ts.map +1 -0
  34. package/lib/qrcode/cipher.js +69 -0
  35. package/lib/qrcode/cipher.js.map +1 -0
  36. package/lib/qrcode/cipher.test.d.ts +2 -0
  37. package/lib/qrcode/cipher.test.d.ts.map +1 -0
  38. package/lib/qrcode/cipher.test.js +40 -0
  39. package/lib/qrcode/cipher.test.js.map +1 -0
  40. package/lib/qrcode/index.d.ts +70 -0
  41. package/lib/qrcode/index.d.ts.map +1 -0
  42. package/lib/qrcode/index.js +312 -0
  43. package/lib/qrcode/index.js.map +1 -0
  44. package/lib/qrcode/index.test.d.ts +2 -0
  45. package/lib/qrcode/index.test.d.ts.map +1 -0
  46. package/lib/qrcode/index.test.js +131 -0
  47. package/lib/qrcode/index.test.js.map +1 -0
  48. package/lib/qrcode/types.d.ts +69 -0
  49. package/lib/qrcode/types.d.ts.map +1 -0
  50. package/lib/qrcode/types.js +3 -0
  51. package/lib/qrcode/types.js.map +1 -0
  52. package/lib/sdk.d.ts +31 -0
  53. package/lib/sdk.d.ts.map +1 -0
  54. package/lib/sdk.js +380 -0
  55. package/lib/sdk.js.map +1 -0
  56. package/lib/store.d.ts +71 -0
  57. package/lib/store.d.ts.map +1 -0
  58. package/lib/store.js +62 -0
  59. package/lib/store.js.map +1 -0
  60. package/lib/types.d.ts +181 -0
  61. package/lib/types.d.ts.map +1 -0
  62. package/lib/types.js +10 -0
  63. package/lib/types.js.map +1 -0
  64. package/lib-es/HWDeviceProvider.d.ts +25 -0
  65. package/lib-es/HWDeviceProvider.d.ts.map +1 -0
  66. package/lib-es/HWDeviceProvider.js +81 -0
  67. package/lib-es/HWDeviceProvider.js.map +1 -0
  68. package/lib-es/api.d.ts +77 -0
  69. package/lib-es/api.d.ts.map +1 -0
  70. package/lib-es/api.js +145 -0
  71. package/lib-es/api.js.map +1 -0
  72. package/lib-es/auth.d.ts +3 -0
  73. package/lib-es/auth.d.ts.map +1 -0
  74. package/lib-es/auth.js +75 -0
  75. package/lib-es/auth.js.map +1 -0
  76. package/lib-es/errors.d.ts +40 -0
  77. package/lib-es/errors.d.ts.map +1 -0
  78. package/lib-es/errors.js +15 -0
  79. package/lib-es/errors.js.map +1 -0
  80. package/lib-es/index.d.ts +6 -0
  81. package/lib-es/index.d.ts.map +1 -0
  82. package/lib-es/index.js +13 -0
  83. package/lib-es/index.js.map +1 -0
  84. package/lib-es/mockSdk.d.ts +22 -0
  85. package/lib-es/mockSdk.d.ts.map +1 -0
  86. package/lib-es/mockSdk.js +201 -0
  87. package/lib-es/mockSdk.js.map +1 -0
  88. package/lib-es/qrcode/cipher.d.ts +12 -0
  89. package/lib-es/qrcode/cipher.d.ts.map +1 -0
  90. package/lib-es/qrcode/cipher.js +61 -0
  91. package/lib-es/qrcode/cipher.js.map +1 -0
  92. package/lib-es/qrcode/cipher.test.d.ts +2 -0
  93. package/lib-es/qrcode/cipher.test.d.ts.map +1 -0
  94. package/lib-es/qrcode/cipher.test.js +38 -0
  95. package/lib-es/qrcode/cipher.test.js.map +1 -0
  96. package/lib-es/qrcode/index.d.ts +70 -0
  97. package/lib-es/qrcode/index.d.ts.map +1 -0
  98. package/lib-es/qrcode/index.js +304 -0
  99. package/lib-es/qrcode/index.js.map +1 -0
  100. package/lib-es/qrcode/index.test.d.ts +2 -0
  101. package/lib-es/qrcode/index.test.d.ts.map +1 -0
  102. package/lib-es/qrcode/index.test.js +126 -0
  103. package/lib-es/qrcode/index.test.js.map +1 -0
  104. package/lib-es/qrcode/types.d.ts +69 -0
  105. package/lib-es/qrcode/types.d.ts.map +1 -0
  106. package/lib-es/qrcode/types.js +2 -0
  107. package/lib-es/qrcode/types.js.map +1 -0
  108. package/lib-es/sdk.d.ts +31 -0
  109. package/lib-es/sdk.d.ts.map +1 -0
  110. package/lib-es/sdk.js +371 -0
  111. package/lib-es/sdk.js.map +1 -0
  112. package/lib-es/store.d.ts +71 -0
  113. package/lib-es/store.d.ts.map +1 -0
  114. package/lib-es/store.js +51 -0
  115. package/lib-es/store.js.map +1 -0
  116. package/lib-es/types.d.ts +181 -0
  117. package/lib-es/types.d.ts.map +1 -0
  118. package/lib-es/types.js +7 -0
  119. package/lib-es/types.js.map +1 -0
  120. package/mocks/scenarios/addSameMemberMultipleTimes.json +426 -0
  121. package/mocks/scenarios/create2trustchainInARow.json +616 -0
  122. package/mocks/scenarios/getOrCreateTransactionCases.json +591 -0
  123. package/mocks/scenarios/member3implicitlyAdded.json +648 -0
  124. package/mocks/scenarios/membersManySelfAdd.json +1427 -0
  125. package/mocks/scenarios/randomMemberTryToDestroy.json +371 -0
  126. package/mocks/scenarios/removeMemberWithTheWrongSeed.json +510 -0
  127. package/mocks/scenarios/removedMemberEjectedOnDeletedTrustchain.json +481 -0
  128. package/mocks/scenarios/removedMemberEjectedOnGetMembers.json +648 -0
  129. package/mocks/scenarios/removedMemberEjectedOnRestore.json +648 -0
  130. package/mocks/scenarios/removingAMemberCreatesAnInteraction.json +593 -0
  131. package/mocks/scenarios/removingYourselfIsForbidden.json +397 -0
  132. package/mocks/scenarios/success.json +978 -0
  133. package/mocks/scenarios/tokenExpires.json +371 -0
  134. package/mocks/scenarios/twoAddMembersFollowedByDeviceAdd.json +705 -0
  135. package/mocks/scenarios/userRefusesAuth.json +40 -0
  136. package/mocks/scenarios/userRefusesRemoveMember.json +542 -0
  137. package/package.json +91 -0
  138. package/scripts/README.md +15 -0
  139. package/scripts/e2e.ts +57 -0
  140. package/src/HWDeviceProvider.ts +105 -0
  141. package/src/__tests__/integration/mock.sdk.test.ts +47 -0
  142. package/src/__tests__/integration/sdk.test.ts +20 -0
  143. package/src/__tests__/tsconfig.json +8 -0
  144. package/src/__tests__/unit/sdk.test.ts +236 -0
  145. package/src/api.ts +202 -0
  146. package/src/auth.ts +81 -0
  147. package/src/errors.ts +18 -0
  148. package/src/index.ts +20 -0
  149. package/src/mockSdk.ts +253 -0
  150. package/src/qrcode/cipher.test.ts +30 -0
  151. package/src/qrcode/cipher.ts +63 -0
  152. package/src/qrcode/index.test.ts +138 -0
  153. package/src/qrcode/index.ts +395 -0
  154. package/src/qrcode/types.ts +70 -0
  155. package/src/sdk.ts +542 -0
  156. package/src/store.ts +99 -0
  157. package/src/types.ts +242 -0
  158. package/tests/scenarios/_template.ts +18 -0
  159. package/tests/scenarios/addSameMemberMultipleTimes.ts +20 -0
  160. package/tests/scenarios/create2trustchainInARow.ts +14 -0
  161. package/tests/scenarios/getOrCreateTransactionCases.ts +74 -0
  162. package/tests/scenarios/member3implicitlyAdded.ts +51 -0
  163. package/tests/scenarios/membersManySelfAdd.ts +18 -0
  164. package/tests/scenarios/randomMemberTryToDestroy.ts +23 -0
  165. package/tests/scenarios/removeMemberWithTheWrongSeed.ts +28 -0
  166. package/tests/scenarios/removedMemberEjectedOnDeletedTrustchain.ts +31 -0
  167. package/tests/scenarios/removedMemberEjectedOnGetMembers.ts +29 -0
  168. package/tests/scenarios/removedMemberEjectedOnRestore.ts +31 -0
  169. package/tests/scenarios/removingAMemberCreatesAnInteraction.ts +42 -0
  170. package/tests/scenarios/removingYourselfIsForbidden.ts +11 -0
  171. package/tests/scenarios/success.ts +94 -0
  172. package/tests/scenarios/tokenExpires.ts +20 -0
  173. package/tests/scenarios/twoAddMembersFollowedByDeviceAdd.ts +49 -0
  174. package/tests/scenarios/userRefusesAuth.ts +28 -0
  175. package/tests/scenarios/userRefusesRemoveMember.ts +66 -0
  176. package/tests/test-helpers/recordTrustchainSdkTests.ts +178 -0
  177. package/tests/test-helpers/replayTrustchainSdkTests.ts +141 -0
  178. package/tests/test-helpers/types.ts +45 -0
  179. package/tests/tsconfig.json +8 -0
  180. package/tsconfig.json +15 -0
package/src/types.ts ADDED
@@ -0,0 +1,242 @@
1
+ import { Observable } from "rxjs";
2
+ import Transport from "@ledgerhq/hw-transport";
3
+ import { TrustchainsResponse } from "./api";
4
+
5
+ /**
6
+ * The JWT is a JSON Web Token that is used to authenticate the user.
7
+ */
8
+ export type JWT = {
9
+ accessToken: string;
10
+ permissions: {
11
+ [trustchainId: string]: {
12
+ [path: string]: string[];
13
+ };
14
+ };
15
+ };
16
+
17
+ /**
18
+ * A function which allow all interactions with the hardware device.
19
+ */
20
+ export type WithDevice = (
21
+ deviceId: string,
22
+ options?: { openTimeoutMs?: number },
23
+ ) => <T>(fn: (transport: Transport) => Observable<T>) => Observable<T>;
24
+
25
+ /**
26
+ * A Trustchain contains the identifier and the contextual data we need to manage members and encrypt/decrypt data.
27
+ */
28
+ export type Trustchain = {
29
+ /**
30
+ * The immutable id of the trustchain root
31
+ */
32
+ rootId: string;
33
+ /**
34
+ * The secret used to encrypt/decrypt the wallet sync data
35
+ */
36
+ walletSyncEncryptionKey: string;
37
+ /**
38
+ * The derivation path on which the current walletSyncEncryptionKey value is stored
39
+ */
40
+ applicationPath: string;
41
+ };
42
+
43
+ /**
44
+ * The Trustchain member credentials are stored on each client, with the privatekey only known by the current client.
45
+ */
46
+ export type MemberCredentials = {
47
+ /**
48
+ * The public key of the member (in hexadecimal)
49
+ */
50
+ pubkey: string;
51
+ /**
52
+ * The private key of the member (in hexadecimal)
53
+ */
54
+ privatekey: string;
55
+ };
56
+
57
+ /**
58
+ * A member of the trustchain
59
+ */
60
+ export type TrustchainMember = {
61
+ /**
62
+ * The id of the member. It corresponds to the MemberCredentials.pubkey
63
+ */
64
+ id: string;
65
+ /**
66
+ * The name of the member as displayed in the UI
67
+ */
68
+ name: string;
69
+ /**
70
+ * a technical permissions of the member. it will often just be Permissions.OWNER
71
+ */
72
+ permissions: number;
73
+ };
74
+
75
+ /**
76
+ * The TrustchainSDKContext is a context that is used to initialize the TrustchainSDK.
77
+ */
78
+ export type TrustchainSDKContext = {
79
+ applicationId: number;
80
+ name: string;
81
+ apiBaseUrl: string;
82
+ };
83
+
84
+ /**
85
+ * provide global callbacks for specific lifecycles.
86
+ * this allows us to decouple trustchain with the rest of Ledger Live.
87
+ * For now, we only introduce very specific hooks we need.
88
+ */
89
+ export type TrustchainLifecycle = {
90
+ /**
91
+ * called when a trustchain rotation is occuring
92
+ * the first function is called when the rotation is starting
93
+ * the second function is called when the rotation is done.
94
+ *
95
+ * in that case, we typically want to delete all other resources depending on it.
96
+ * we do this with the existing jwt token before refreshing it.
97
+ */
98
+ onTrustchainRotation: (
99
+ trustchainSdk: TrustchainSDK,
100
+ oldTrustchain: Trustchain,
101
+ memberCredentials: MemberCredentials,
102
+ ) => Promise<(newTrustchain: Trustchain) => Promise<void>>;
103
+ };
104
+
105
+ export enum TrustchainResultType {
106
+ created = "created",
107
+ updated = "updated",
108
+ restored = "restored",
109
+ }
110
+
111
+ /**
112
+ * the trustchain with a result type indicating what happened during getOrCreateTrustchain
113
+ */
114
+ export type TrustchainResult =
115
+ | {
116
+ // the trustchain didn't exist and was created
117
+ type: TrustchainResultType.created;
118
+ trustchain: Trustchain;
119
+ }
120
+ | {
121
+ // the trustchain already existed and was updated (typically the current member was added)
122
+ type: TrustchainResultType.updated;
123
+ trustchain: Trustchain;
124
+ }
125
+ | {
126
+ // the trustchain existed and was just retrieved (no need to update it)
127
+ type: TrustchainResultType.restored;
128
+ trustchain: Trustchain;
129
+ };
130
+
131
+ /**
132
+ * cache (default): the SDK will use the cached JWT if it's still valid, otherwise it will refresh it.
133
+ * refresh: the SDK will always refresh the JWT if possible.
134
+ * no-cache: the SDK will always request a new JWT.
135
+ */
136
+ export type AuthCachePolicy = "no-cache" | "refresh" | "cache";
137
+
138
+ /**
139
+ * The main interface for the UI to interact with the trustchain protocol.
140
+ *
141
+ * @example
142
+ *
143
+ * import { sdk } from "@ledgerhq/ledger-key-ring-protocol";
144
+ *
145
+ * sdk.getOrCreateTrustchain(deviceId, memberCredentials).then(trustchain => console.log(trustchain));
146
+ */
147
+ export interface TrustchainSDK {
148
+ /**
149
+ * Generate the live credentials that represents a Live instance, member of the trustchain.
150
+ * This method is expected to be used the first time Ledger Live is opened (if Live never generated them before) and then persisted over the future user sessions of Ledger Live in order for the member to be able to authenticate and manage the trustchain.
151
+ */
152
+ initMemberCredentials(): Promise<MemberCredentials>;
153
+
154
+ /**
155
+ * Access a JWT from the TrustchainSDK. manage the reauthentication if needed.
156
+ * A trustchain must have been created and the Live instance must have been added as a member.
157
+ * The returned token will typically be used for regular operations like wallet sync.
158
+ */
159
+ withAuth<T>(
160
+ trustchain: Trustchain,
161
+ memberCredentials: MemberCredentials,
162
+ f: (jwt: JWT) => Promise<T>,
163
+ policy?: AuthCachePolicy,
164
+ ignorePermissionsChecks?: boolean,
165
+ ): Promise<T>;
166
+
167
+ /**
168
+ * This method will either create the required trustchains (root and application) or restore them.
169
+ * The returned trustchain will be initialized on the root level and also will have the branch derivation corresponding to the contextual applicationId.
170
+ * It will also have the wallet sync encryption key initialized.
171
+ * The latest jwt is also returned because it was potentially updated during the process.
172
+ */
173
+ getOrCreateTrustchain(
174
+ deviceId: string,
175
+ memberCredentials: MemberCredentials,
176
+ callbacks?: GetOrCreateTrustchainCallbacks,
177
+ topic?: Uint8Array,
178
+ currentTrustchain?: Trustchain,
179
+ ): Promise<TrustchainResult>;
180
+
181
+ /**
182
+ * Restore the current trustchain encryption key, typically due to a key rotation.
183
+ */
184
+ restoreTrustchain(
185
+ trustchain: Trustchain,
186
+ memberCredentials: MemberCredentials,
187
+ ): Promise<Trustchain>;
188
+
189
+ /**
190
+ * list the current members of the application trustchain
191
+ */
192
+ getMembers(
193
+ trustchain: Trustchain,
194
+ memberCredentials: MemberCredentials,
195
+ ): Promise<TrustchainMember[]>;
196
+
197
+ /**
198
+ * remove a member from the application trustchain
199
+ */
200
+ removeMember(
201
+ deviceId: string,
202
+ trustchain: Trustchain,
203
+ memberCredentials: MemberCredentials,
204
+ member: TrustchainMember,
205
+ callbacks?: TrustchainDeviceCallbacks,
206
+ ): Promise<Trustchain>;
207
+
208
+ /**
209
+ * add a member to the application trustchain
210
+ */
211
+ addMember(
212
+ trustchain: Trustchain,
213
+ memberCredentials: MemberCredentials,
214
+ member: TrustchainMember,
215
+ ): Promise<void>;
216
+
217
+ /**
218
+ * destroy the trustchain
219
+ */
220
+ destroyTrustchain(trustchain: Trustchain, memberCredentials: MemberCredentials): Promise<void>;
221
+
222
+ /**
223
+ * encrypt data with the trustchain encryption key
224
+ */
225
+ encryptUserData(trustchain: Trustchain, obj: object): Promise<Uint8Array>;
226
+
227
+ /**
228
+ * decrypt data with the trustchain encryption key
229
+ */
230
+ decryptUserData(trustchain: Trustchain, data: Uint8Array): Promise<Uint8Array>;
231
+
232
+ invalidateJwt(): void;
233
+ }
234
+
235
+ export interface TrustchainDeviceCallbacks {
236
+ onStartRequestUserInteraction?: () => void;
237
+ onEndRequestUserInteraction?: () => void;
238
+ }
239
+
240
+ export interface GetOrCreateTrustchainCallbacks extends TrustchainDeviceCallbacks {
241
+ onInitialResponse?: (trustchains: TrustchainsResponse) => void;
242
+ }
@@ -0,0 +1,18 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ /**
5
+ * Edit this code to the test you want.
6
+ * This script will be used both as a end-to-end tests and unit tests.
7
+ * The end-to-end tests are used to generate mock for the same unit tests.
8
+ */
9
+ const name1 = "cli-member1";
10
+ const sdk1 = sdkForName(name1);
11
+ const memberCredentials = await sdk1.initMemberCredentials();
12
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, memberCredentials);
13
+
14
+ await sdk1.destroyTrustchain(trustchain, memberCredentials);
15
+ }
16
+
17
+ // this can overrides the default config used by the recorder
18
+ export const recorderConfig = {};
@@ -0,0 +1,20 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ const name1 = "Member 1";
5
+ const sdk = sdkForName(name1);
6
+ const member1creds = await sdk.initMemberCredentials();
7
+ const { trustchain } = await sdk.getOrCreateTrustchain(deviceId, member1creds);
8
+
9
+ const name2 = "Member 2";
10
+ const member2creds = await sdk.initMemberCredentials();
11
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
12
+
13
+ await sdk.addMember(trustchain, member1creds, member2);
14
+ await sdk.addMember(trustchain, member1creds, member2);
15
+
16
+ const members = await sdk.getMembers(trustchain, member1creds);
17
+ await sdk.destroyTrustchain(trustchain, member1creds);
18
+
19
+ expect(members.length).toBe(2);
20
+ }
@@ -0,0 +1,14 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ const sdk = sdkForName("Foo");
5
+ const creds = await sdk.initMemberCredentials();
6
+
7
+ const t1 = await sdk.getOrCreateTrustchain(deviceId, creds);
8
+ await sdk.destroyTrustchain(t1.trustchain, creds);
9
+ expect(t1.type).toBe("created");
10
+
11
+ const t2 = await sdk.getOrCreateTrustchain(deviceId, creds);
12
+ await sdk.destroyTrustchain(t2.trustchain, creds);
13
+ expect(t2.type).toBe("created");
14
+ }
@@ -0,0 +1,74 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ const name1 = "Member 1";
5
+ const sdk1 = sdkForName(name1);
6
+ const member1creds = await sdk1.initMemberCredentials();
7
+
8
+ let interactionCounter = 0;
9
+ let totalInteractionCounter = 0;
10
+ const callbacks = {
11
+ onStartRequestUserInteraction: () => {
12
+ totalInteractionCounter++;
13
+ interactionCounter++;
14
+ },
15
+ onEndRequestUserInteraction: () => {
16
+ interactionCounter--;
17
+ },
18
+ };
19
+
20
+ // verify that getOrCreateTrustchain is idempotent
21
+ const { trustchain: t1, type: type1 } = await sdk1.getOrCreateTrustchain(
22
+ deviceId,
23
+ member1creds,
24
+ callbacks,
25
+ );
26
+ expect(type1).toBe("created");
27
+ expect(totalInteractionCounter).toBe(2); // there are two interaction: one for device auth, one for trustchain addition
28
+ const { trustchain: t2, type: type2 } = await sdk1.getOrCreateTrustchain(
29
+ deviceId,
30
+ member1creds,
31
+ callbacks,
32
+ );
33
+ expect(type2).toBe("restored");
34
+ expect(totalInteractionCounter).toBe(3); // one more device auth interaction happened
35
+ expect(t1).toEqual(t2);
36
+
37
+ // verify that a second member can join the trustchain and get the same trustchain
38
+ const name2 = "Member 2";
39
+ const sdk2 = sdkForName(name2);
40
+ const member2creds = await sdk2.initMemberCredentials();
41
+ const { trustchain: t3, type: type3 } = await sdk2.getOrCreateTrustchain(
42
+ deviceId,
43
+ member2creds,
44
+ callbacks,
45
+ );
46
+ expect(type3).toBe("updated");
47
+ expect(t1).toEqual(t3);
48
+
49
+ // check there are indeed our two members in the trustchain
50
+ const members = await sdk1.getMembers(t1, member1creds);
51
+ expect(members).toEqual([
52
+ {
53
+ id: member1creds.pubkey,
54
+ name: name1,
55
+ permissions: 0xffffffff,
56
+ },
57
+ {
58
+ id: member2creds.pubkey,
59
+ name: name2,
60
+ permissions: 0xffffffff,
61
+ },
62
+ ]);
63
+
64
+ // destroy
65
+ await sdk1.destroyTrustchain(t1, member1creds);
66
+
67
+ expect({
68
+ interactionCounter,
69
+ totalInteractionCounter,
70
+ }).toEqual({
71
+ interactionCounter: 0, // total of interaction+- is back at 0
72
+ totalInteractionCounter: 5,
73
+ });
74
+ }
@@ -0,0 +1,51 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ /**
4
+ * a complete scenario with 3 members and various sdk successful interactions.
5
+ */
6
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
7
+ // members
8
+ const name1 = "Member 1";
9
+ const sdk1 = sdkForName(name1);
10
+ const member1creds = await sdk1.initMemberCredentials();
11
+
12
+ const name2 = "Member 2";
13
+ const sdk2 = sdkForName(name2);
14
+ const member2creds = await sdk2.initMemberCredentials();
15
+
16
+ const name3 = "Member 3";
17
+ const sdk3 = sdkForName(name3);
18
+ const member3creds = await sdk3.initMemberCredentials();
19
+
20
+ // auth with the device and init the first trustchain
21
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
22
+
23
+ // member 1 adds member 2
24
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
25
+ await sdk1.addMember(trustchain, member1creds, member2);
26
+
27
+ // member 3 do a getOrCreateTrustchain that should add itself implicitly
28
+ const { trustchain: trustchain3 } = await sdk3.getOrCreateTrustchain(deviceId, member3creds);
29
+
30
+ // list members
31
+ const members = await sdk3.getMembers(trustchain3, member3creds);
32
+ expect(members).toEqual([
33
+ {
34
+ id: member1creds.pubkey,
35
+ name: name1,
36
+ permissions: 0xffffffff,
37
+ },
38
+ {
39
+ id: member2creds.pubkey,
40
+ name: name2,
41
+ permissions: 0xffffffff,
42
+ },
43
+ {
44
+ id: member3creds.pubkey,
45
+ name: name3,
46
+ permissions: 0xffffffff,
47
+ },
48
+ ]);
49
+
50
+ await sdk2.destroyTrustchain(trustchain, member2creds);
51
+ }
@@ -0,0 +1,18 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ let trustchainId;
5
+ const n = 10;
6
+ for (let i = 1; i < n; i++) {
7
+ const name = "Member " + i;
8
+ const sdk = sdkForName(name);
9
+ const creds = await sdk.initMemberCredentials();
10
+ const { trustchain } = await sdk.getOrCreateTrustchain(deviceId, creds);
11
+ if (!trustchainId) trustchainId = trustchain.rootId;
12
+ expect(trustchain.rootId).toBe(trustchainId);
13
+ if (i === n - 1) {
14
+ // cleanup
15
+ await sdk.destroyTrustchain(trustchain, creds);
16
+ }
17
+ }
18
+ }
@@ -0,0 +1,23 @@
1
+ import { TrustchainEjected } from "../../src/errors";
2
+ import { ScenarioOptions } from "../test-helpers/types";
3
+
4
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
5
+ // first member initializes itself
6
+ const name1 = "Member 1";
7
+ const sdk1 = sdkForName(name1);
8
+ const member1creds = await sdk1.initMemberCredentials();
9
+
10
+ // second member initializes itself
11
+ const name2 = "Member 2";
12
+ const sdk2 = sdkForName(name2);
13
+ const member2creds = await sdk2.initMemberCredentials();
14
+
15
+ // auth with the device and init the first trustchain
16
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
17
+
18
+ // now member2 will get an ejected error when trying to destroy the trustchain
19
+ await expect(sdk2.destroyTrustchain(trustchain, member2creds)).rejects.toThrow(TrustchainEjected);
20
+
21
+ // member1 can destroy the trustchain
22
+ await sdk1.destroyTrustchain(trustchain, member1creds);
23
+ }
@@ -0,0 +1,28 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+ import { TrustchainNotAllowed } from "../../src/errors";
3
+
4
+ export async function scenario(
5
+ deviceId: string,
6
+ { sdkForName, switchDeviceSeed }: ScenarioOptions,
7
+ ) {
8
+ const name1 = "Member 1";
9
+ const sdk1 = sdkForName(name1);
10
+ const member1creds = await sdk1.initMemberCredentials();
11
+ const member1 = { name: name1, id: member1creds.pubkey, permissions: 0xffffffff };
12
+
13
+ const name2 = "Member 2";
14
+ const sdk2 = sdkForName(name2);
15
+ const member2creds = await sdk2.initMemberCredentials();
16
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
17
+
18
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
19
+ await sdk1.addMember(trustchain, member1creds, member2);
20
+
21
+ const device = await switchDeviceSeed();
22
+
23
+ await expect(sdk1.removeMember(device.id, trustchain, member2creds, member1)).rejects.toThrow(
24
+ TrustchainNotAllowed,
25
+ );
26
+
27
+ await sdk2.destroyTrustchain(trustchain, member2creds);
28
+ }
@@ -0,0 +1,31 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ // first member initializes itself
5
+ const name1 = "Member 1";
6
+ const sdk1 = sdkForName(name1);
7
+ const member1creds = await sdk1.initMemberCredentials();
8
+ // second member initializes itself
9
+ const name2 = "Member 2";
10
+ const sdk2 = sdkForName(name2);
11
+ const member2creds = await sdk2.initMemberCredentials();
12
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
13
+
14
+ // auth with the device and init the first trustchain
15
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
16
+
17
+ // member 1 adds member 2 (= qr code flow)
18
+ await sdk1.addMember(trustchain, member1creds, member2);
19
+
20
+ // member2 can get the members
21
+ await sdk2.getMembers(trustchain, member2creds);
22
+
23
+ // member1 removes trustchain
24
+ await sdk1.destroyTrustchain(trustchain, member1creds);
25
+
26
+ // force a refresh to happen (which normally happen after some time)
27
+ await sdk2.withAuth(trustchain, member2creds, jwt => Promise.resolve(jwt), "refresh", true);
28
+
29
+ // member2 is no longer a member so is not authorized to get the members
30
+ await expect(sdk2.getMembers(trustchain, member2creds)).rejects.toThrow();
31
+ }
@@ -0,0 +1,29 @@
1
+ import { TrustchainEjected } from "../../src/errors";
2
+ import { ScenarioOptions } from "../test-helpers/types";
3
+
4
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
5
+ // first member initializes itself
6
+ const name1 = "Member 1";
7
+ const sdk1 = sdkForName(name1);
8
+ const member1creds = await sdk1.initMemberCredentials();
9
+ // second member initializes itself
10
+ const name2 = "Member 2";
11
+ const sdk2 = sdkForName(name2);
12
+ const member2creds = await sdk2.initMemberCredentials();
13
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
14
+
15
+ // auth with the device and init the first trustchain
16
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
17
+
18
+ // member 1 adds member 2 (= qr code flow)
19
+ await sdk1.addMember(trustchain, member1creds, member2);
20
+
21
+ // member1 removes member2
22
+ const newTrustchain = await sdk1.removeMember(deviceId, trustchain, member1creds, member2);
23
+
24
+ // member2 is no longer a member so is not authorized to get the members
25
+ await expect(sdk2.getMembers(trustchain, member2creds)).rejects.toThrow(TrustchainEjected);
26
+
27
+ // member3 destroy the trustchain
28
+ await sdk1.destroyTrustchain(newTrustchain, member1creds);
29
+ }
@@ -0,0 +1,31 @@
1
+ import { TrustchainEjected } from "../../src/errors";
2
+ import { ScenarioOptions } from "../test-helpers/types";
3
+
4
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
5
+ // first member initializes itself
6
+ const name1 = "Member 1";
7
+ const sdk1 = sdkForName(name1);
8
+ const member1creds = await sdk1.initMemberCredentials();
9
+
10
+ // auth with the device and init the first trustchain
11
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
12
+
13
+ // second member initializes itself
14
+ const name2 = "Member 2";
15
+ const sdk2 = sdkForName(name2);
16
+ const member2creds = await sdk2.initMemberCredentials();
17
+
18
+ // member 1 adds member 2 (= qr code flow)
19
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
20
+ await sdk1.addMember(trustchain, member1creds, member2);
21
+
22
+ // member1 removes member2
23
+ const newTrustchain = await sdk1.removeMember(deviceId, trustchain, member1creds, member2);
24
+
25
+ // now member2 will get an ejected error when trying to restore the trustchain
26
+ // NB: restoreTrustchain is typically what we would call when member2 see that the encryptionKey no longer work after the rotation
27
+ await expect(sdk2.restoreTrustchain(trustchain, member2creds)).rejects.toThrow(TrustchainEjected);
28
+
29
+ // member3 destroy the trustchain
30
+ await sdk1.destroyTrustchain(newTrustchain, member1creds);
31
+ }
@@ -0,0 +1,42 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ const name1 = "Member 1";
5
+ const sdk1 = sdkForName(name1);
6
+ const member1creds = await sdk1.initMemberCredentials();
7
+
8
+ const name2 = "Member 2";
9
+ const sdk2 = sdkForName(name2);
10
+ const member2creds = await sdk2.initMemberCredentials();
11
+ const member2 = { name: name2, id: member2creds.pubkey, permissions: 0xffffffff };
12
+
13
+ let interactionCounter = 0;
14
+ let totalInteractionCounter = 0;
15
+ const callbacks = {
16
+ onStartRequestUserInteraction: () => {
17
+ totalInteractionCounter++;
18
+ interactionCounter++;
19
+ },
20
+ onEndRequestUserInteraction: () => {
21
+ interactionCounter--;
22
+ },
23
+ };
24
+
25
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds, callbacks);
26
+ expect(totalInteractionCounter).toBe(2); // there are two interaction: one for device auth, one for trustchain addition
27
+
28
+ await sdk1.addMember(trustchain, member1creds, member2);
29
+
30
+ const newTrustchain = await sdk1.removeMember(
31
+ deviceId,
32
+ trustchain,
33
+ member1creds,
34
+ member2,
35
+ callbacks,
36
+ );
37
+ expect(totalInteractionCounter).toBe(5); // there are 2 interactions for trustchain addition
38
+
39
+ // destroy the trustchain
40
+ await sdk1.destroyTrustchain(newTrustchain, member1creds);
41
+ expect(interactionCounter).toBe(0); // the start/stop is coherent
42
+ }
@@ -0,0 +1,11 @@
1
+ import { ScenarioOptions } from "../test-helpers/types";
2
+
3
+ export async function scenario(deviceId: string, { sdkForName }: ScenarioOptions) {
4
+ const name1 = "Member 1";
5
+ const sdk1 = sdkForName(name1);
6
+ const member1creds = await sdk1.initMemberCredentials();
7
+ const { trustchain } = await sdk1.getOrCreateTrustchain(deviceId, member1creds);
8
+ const members = await sdk1.getMembers(trustchain, member1creds);
9
+ await expect(sdk1.removeMember(deviceId, trustchain, member1creds, members[0])).rejects.toThrow();
10
+ await sdk1.destroyTrustchain(trustchain, member1creds);
11
+ }