@holochain/client 0.19.0-dev.1 → 0.19.0-dev.3

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.
@@ -1,5 +1,5 @@
1
1
  import { UnsubscribeFunction } from "emittery";
2
- import { AgentPubKey, AppAuthenticationToken, AppInfo, CapSecret, CellId, ClonedCell, DnaHash, DnaProperties, EntryHash, FunctionName, InstalledAppId, MembraneProof, MemproofMap, NetworkInfo, NetworkSeed, Nonce256Bit, RoleName, Timestamp, Transformer, WebsocketConnectionOptions, ZomeName } from "../../index.js";
2
+ import { AgentPubKey, AppAuthenticationToken, AppInfo, CapSecret, CellId, ClonedCell, DnaHash, DnaProperties, EntryHash, FunctionName, InstalledAppId, MembraneProof, MemproofMap, NetworkInfo, NetworkSeed, Nonce256Bit, RoleName, Timestamp, Transformer, WebsocketConnectionOptions, ZomeName, PreflightRequest, SignedAction, SignedActionHashed } from "../../index.js";
3
3
  /**
4
4
  * @public
5
5
  */
@@ -19,7 +19,7 @@ export type RoleNameCallZomeRequestSigned = Omit<CallZomeRequestSigned, "cell_id
19
19
  /**
20
20
  * @public
21
21
  */
22
- export type AppCallZomeRequest = NonProvenanceCallZomeRequest | RoleNameCallZomeRequest | CallZomeRequestSigned | RoleNameCallZomeRequestSigned;
22
+ export type AppCallZomeRequest = NonProvenanceCallZomeRequest | RoleNameCallZomeRequest | CallZomeRequest;
23
23
  /**
24
24
  * @public
25
25
  */
@@ -45,7 +45,7 @@ export interface AppEvents {
45
45
  /**
46
46
  * @public
47
47
  */
48
- export interface CallZomeRequestUnsigned extends CallZomeRequest {
48
+ export interface CallZomeRequestParams extends CallZomeRequest {
49
49
  cap_secret: CapSecret | null;
50
50
  nonce: Nonce256Bit;
51
51
  expires_at: number;
@@ -53,7 +53,8 @@ export interface CallZomeRequestUnsigned extends CallZomeRequest {
53
53
  /**
54
54
  * @public
55
55
  */
56
- export interface CallZomeRequestSigned extends CallZomeRequestUnsigned {
56
+ export interface CallZomeRequestSigned {
57
+ bytes: Uint8Array;
57
58
  signature: Uint8Array;
58
59
  }
59
60
  /**
@@ -183,6 +184,211 @@ export interface NetworkInfoRequest {
183
184
  */
184
185
  last_time_queried?: number;
185
186
  }
187
+ /**
188
+ * Cell id for which the countersigning session state is requested.
189
+ *
190
+ * @public
191
+ */
192
+ export type GetCountersigningSessionStateRequest = CellId;
193
+ /**
194
+ * @public
195
+ */
196
+ export type GetCountersigningSessionStateResponse = null | CountersigningSessionState;
197
+ /**
198
+ * Cell id for which the countersigning session should be abandoned.
199
+ *
200
+ * @public
201
+ */
202
+ export type AbandonCountersigningSessionStateRequest = CellId;
203
+ /**
204
+ * @public
205
+ */
206
+ export type AbandonCountersigningSessionStateResponse = null;
207
+ /**
208
+ * Cell id for which the countersigning session should be published.
209
+ *
210
+ * @public
211
+ */
212
+ export type PublishCountersigningSessionStateRequest = CellId;
213
+ /**
214
+ * @public
215
+ */
216
+ export type PublishCountersigningSessionStateResponse = null;
217
+ /**
218
+ * @public
219
+ */
220
+ export declare enum CountersigningSessionStateType {
221
+ Accepted = "Accepted",
222
+ SignaturesCollected = "SignaturesCollected",
223
+ Unknown = "Unknown"
224
+ }
225
+ /**
226
+ * @public
227
+ */
228
+ export type CountersigningSessionState =
229
+ /**
230
+ * This is the entry state. Accepting a countersigning session through the HDK will immediately
231
+ * register the countersigning session in this state, for management by the countersigning workflow.
232
+ *
233
+ * The session will stay in this state even when the agent commits their countersigning entry and only
234
+ * move to the next state when the first signature bundle is received.
235
+ */
236
+ {
237
+ [CountersigningSessionStateType.Accepted]: PreflightRequest;
238
+ }
239
+ /**
240
+ * This is the state where we have collected one or more signatures for a countersigning session.
241
+ *
242
+ * This state can be entered from the [CountersigningSessionState::Accepted] state, which happens
243
+ * when a witness returns a signature bundle to us. While the session has not timed out, we will
244
+ * stay in this state and wait until one of the signatures bundles we have received is valid for
245
+ * the session to be completed.
246
+ *
247
+ * If we entered this state from the [CountersigningSessionState::Accepted] state, we will either
248
+ * complete the session successfully or the session will time out. On a timeout we will move
249
+ * to the [CountersigningSessionState::Unknown] for a limited number of attempts to recover the session.
250
+ *
251
+ * This state can also be entered from the [CountersigningSessionState::Unknown] state, which happens when we
252
+ * have been able to recover the session from the source chain and have requested signed actions
253
+ * from agent authorities to build a signature bundle.
254
+ *
255
+ * If we entered this state from the [CountersigningSessionState::Unknown] state, we will either
256
+ * complete the session successfully, or if the signatures are invalid, we will return to the
257
+ * [CountersigningSessionState::Unknown] state.
258
+ */
259
+ | {
260
+ [CountersigningSessionStateType.SignaturesCollected]: {
261
+ /** The preflight request that has been exchanged among countersigning peers. */
262
+ preflight_request: PreflightRequest;
263
+ /** Signed actions of the committed countersigned entries of all participating peers. */
264
+ signature_bundles: SignedAction[][];
265
+ /**
266
+ * This field is set when the signature bundle came from querying agent activity authorities
267
+ * in the unknown state. If we started from that state, we should return to it if the
268
+ * signature bundle is invalid. Otherwise, stay in this state and wait for more signatures.
269
+ */
270
+ resolution?: SessionResolutionSummary;
271
+ };
272
+ }
273
+ /**
274
+ * The session is in an unknown state and needs to be resolved.
275
+ *
276
+ * This state is used when we have lost track of the countersigning session. This happens if
277
+ * we have got far enough to create the countersigning entry but have crashed or restarted
278
+ * before we could complete the session. In this case we need to try to discover what the other
279
+ * agent or agents involved in the session have done.
280
+ *
281
+ * This state is also entered temporarily when we have published a signature and then the
282
+ * session has timed out. To avoid deadlocking with two parties both waiting for each other to
283
+ * proceed, we cannot stay in this state indefinitely. We will make a limited number of attempts
284
+ * to recover and if we cannot, we will abandon the session.
285
+ *
286
+ * The only exception to the attempt limiting is if we are unable to reach agent activity authorities
287
+ * to progress resolving the session. In this case, the attempts are not counted towards the
288
+ * configured limit. This does not protect us against a network partition where we can only see
289
+ * a subset of the network, but it does protect us against Holochain forcing a decision while
290
+ * it is unable to reach any peers.
291
+ *
292
+ * Note that because the [PreflightRequest] is stored here, we only ever enter the unknown state
293
+ * if we managed to keep the preflight request in memory, or if we have been able to recover it
294
+ * from the source chain as part of the committed [CounterSigningSessionData]. Otherwise, we
295
+ * are unable to discover what session we were participating in, and we must abandon the session
296
+ * without going through this recovery state.
297
+ */
298
+ | {
299
+ [CountersigningSessionStateType.Unknown]: {
300
+ /** The preflight request that has been exchanged. */
301
+ preflight_request: PreflightRequest;
302
+ /** Summary of the attempts to resolve this session. */
303
+ resolution: SessionResolutionSummary;
304
+ /** Flag if the session is programmed to be force-abandoned on the next countersigning workflow run. */
305
+ force_abandon: boolean;
306
+ /** Flag if the session is programmed to be force-published on the next countersigning workflow run. */
307
+ force_publish: boolean;
308
+ };
309
+ };
310
+ /**
311
+ * Summary of the workflow's attempts to resolve the outcome a failed countersigning session.
312
+ * This tracks the numbers of attempts and the outcome of the most recent attempt.
313
+ *
314
+ * @public
315
+ */
316
+ export interface SessionResolutionSummary {
317
+ /** The reason why session resolution is required. */
318
+ required_reason: ResolutionRequiredReason;
319
+ /**
320
+ * How many attempts have been made to resolve the session.
321
+ *
322
+ * Attempts are made according to the frequency specified by [RETRY_UNKNOWN_SESSION_STATE_DELAY].
323
+ *
324
+ * This count is only correct for the current run of the Holochain conductor. If the conductor
325
+ * is restarted then this counter is also reset.
326
+ */
327
+ attempts: number;
328
+ /** The time of the last attempt to resolve the session. */
329
+ last_attempt_at?: Timestamp;
330
+ /** The outcome of the most recent attempt to resolve the session. */
331
+ outcomes: SessionResolutionOutcome[];
332
+ }
333
+ /**
334
+ * The reason why a countersigning session can not be resolved automatically and requires manual resolution.
335
+ *
336
+ * @public
337
+ */
338
+ export declare enum ResolutionRequiredReason {
339
+ /** The session has timed out, so we should try to resolve its state before abandoning. */
340
+ Timeout = "Timeout",
341
+ /** Something happened, like a conductor restart, and we lost track of the session. */
342
+ Unknown = "Unknown"
343
+ }
344
+ /**
345
+ * The outcome for a single agent who participated in a countersigning session.
346
+ *
347
+ * [NUM_AUTHORITIES_TO_QUERY] authorities are made to agent activity authorities for each agent,
348
+ * and the decisions are collected into [SessionResolutionOutcome::decisions].
349
+ *
350
+ * @public
351
+ */
352
+ export interface SessionResolutionOutcome {
353
+ /**
354
+ * The agent who participated in the countersigning session and is the subject of this
355
+ * resolution outcome.
356
+ */
357
+ agent: AgentPubKey;
358
+ /** The resolved decision for each authority for the subject agent. */
359
+ decisions: SessionCompletionDecision[];
360
+ }
361
+ /**
362
+ * Decision about an incomplete countersigning session.
363
+ *
364
+ * @public
365
+ */
366
+ export declare enum SessionCompletionDecisionType {
367
+ /** Evidence found on the network that this session completed successfully. */
368
+ Complete = "Complete",
369
+ /**
370
+ * Evidence found on the network that this session was abandoned and other agents have
371
+ * added to their chain without completing the session.
372
+ */
373
+ Abandoned = "Abandoned",
374
+ /**
375
+ * No evidence, or inconclusive evidence, was found on the network. Holochain will not make an
376
+ * automatic decision until the evidence is conclusive.
377
+ */
378
+ Indeterminate = "Indeterminate",
379
+ /**There were errors encountered while trying to resolve the session. Errors such as network
380
+ * errors are treated differently to inconclusive evidence. We don't want to force a decision
381
+ * when we're offline, for example. In this case, the resolution must be retried later and this
382
+ * attempt should not be counted.
383
+ */
384
+ Failed = "Failed"
385
+ }
386
+ /**
387
+ * @public
388
+ */
389
+ export type SessionCompletionDecision = {
390
+ [SessionCompletionDecisionType.Complete]: SignedActionHashed;
391
+ } | SessionCompletionDecisionType.Abandoned | SessionCompletionDecisionType.Indeterminate | SessionCompletionDecisionType.Failed;
186
392
  /**
187
393
  * @public
188
394
  */
@@ -1,3 +1,50 @@
1
+ /**
2
+ * @public
3
+ */
4
+ export var CountersigningSessionStateType;
5
+ (function (CountersigningSessionStateType) {
6
+ CountersigningSessionStateType["Accepted"] = "Accepted";
7
+ CountersigningSessionStateType["SignaturesCollected"] = "SignaturesCollected";
8
+ CountersigningSessionStateType["Unknown"] = "Unknown";
9
+ })(CountersigningSessionStateType || (CountersigningSessionStateType = {}));
10
+ /**
11
+ * The reason why a countersigning session can not be resolved automatically and requires manual resolution.
12
+ *
13
+ * @public
14
+ */
15
+ export var ResolutionRequiredReason;
16
+ (function (ResolutionRequiredReason) {
17
+ /** The session has timed out, so we should try to resolve its state before abandoning. */
18
+ ResolutionRequiredReason["Timeout"] = "Timeout";
19
+ /** Something happened, like a conductor restart, and we lost track of the session. */
20
+ ResolutionRequiredReason["Unknown"] = "Unknown";
21
+ })(ResolutionRequiredReason || (ResolutionRequiredReason = {}));
22
+ /**
23
+ * Decision about an incomplete countersigning session.
24
+ *
25
+ * @public
26
+ */
27
+ export var SessionCompletionDecisionType;
28
+ (function (SessionCompletionDecisionType) {
29
+ /** Evidence found on the network that this session completed successfully. */
30
+ SessionCompletionDecisionType["Complete"] = "Complete";
31
+ /**
32
+ * Evidence found on the network that this session was abandoned and other agents have
33
+ * added to their chain without completing the session.
34
+ */
35
+ SessionCompletionDecisionType["Abandoned"] = "Abandoned";
36
+ /**
37
+ * No evidence, or inconclusive evidence, was found on the network. Holochain will not make an
38
+ * automatic decision until the evidence is conclusive.
39
+ */
40
+ SessionCompletionDecisionType["Indeterminate"] = "Indeterminate";
41
+ /**There were errors encountered while trying to resolve the session. Errors such as network
42
+ * errors are treated differently to inconclusive evidence. We don't want to force a decision
43
+ * when we're offline, for example. In this case, the resolution must be retried later and this
44
+ * attempt should not be counted.
45
+ */
46
+ SessionCompletionDecisionType["Failed"] = "Failed";
47
+ })(SessionCompletionDecisionType || (SessionCompletionDecisionType = {}));
1
48
  /**
2
49
  * @public
3
50
  */
@@ -1,7 +1,7 @@
1
1
  import { UnsubscribeFunction } from "emittery";
2
2
  import { AgentPubKey, CellId, InstalledAppId, RoleName } from "../../types.js";
3
3
  import { AppInfo, MemproofMap } from "../admin/index.js";
4
- import { AppCallZomeRequest, AppClient, AppEvents, AppNetworkInfoRequest, AppCreateCloneCellRequest, AppDisableCloneCellRequest, AppEnableCloneCellRequest, SignalCb, CallZomeRequest, CallZomeRequestSigned, CallZomeResponse, CreateCloneCellResponse, DisableCloneCellResponse, EnableCloneCellResponse, NetworkInfoResponse, AppWebsocketConnectionOptions } from "./types.js";
4
+ import { AppCallZomeRequest, AppClient, AppEvents, AppNetworkInfoRequest, AppCreateCloneCellRequest, AppDisableCloneCellRequest, AppEnableCloneCellRequest, SignalCb, CallZomeRequest, CallZomeRequestSigned, NetworkInfoResponse, AppWebsocketConnectionOptions, GetCountersigningSessionStateRequest, GetCountersigningSessionStateResponse, AbandonCountersigningSessionStateRequest, PublishCountersigningSessionStateRequest } from "./types.js";
5
5
  import { WsClient } from "../client.js";
6
6
  /**
7
7
  * A class to establish a websocket connection to an App interface, for a
@@ -16,7 +16,6 @@ export declare class AppWebsocket implements AppClient {
16
16
  private readonly defaultTimeout;
17
17
  private readonly emitter;
18
18
  private readonly callZomeTransform;
19
- private readonly appAuthenticationToken;
20
19
  cachedAppInfo?: AppInfo | null;
21
20
  private readonly appInfoRequester;
22
21
  private readonly callZomeRequester;
@@ -26,6 +25,9 @@ export declare class AppWebsocket implements AppClient {
26
25
  private readonly enableCloneCellRequester;
27
26
  private readonly disableCloneCellRequester;
28
27
  private readonly networkInfoRequester;
28
+ private readonly getCountersigningSessionStateRequester;
29
+ private readonly abandonCountersigningSessionRequester;
30
+ private readonly publishCountersigningSessionRequester;
29
31
  private constructor();
30
32
  /**
31
33
  * Instance factory for creating an {@link AppWebsocket}.
@@ -68,33 +70,111 @@ export declare class AppWebsocket implements AppClient {
68
70
  * @param timeout - A timeout to override the default.
69
71
  * @returns The zome call's response.
70
72
  */
71
- callZome(request: AppCallZomeRequest, timeout?: number): Promise<CallZomeResponse>;
73
+ callZome<ReturnType>(request: AppCallZomeRequest, timeout?: number): Promise<ReturnType>;
72
74
  /**
73
75
  * Clone an existing provisioned cell.
74
76
  *
75
77
  * @param args - Specify the cell to clone.
76
78
  * @returns The created clone cell.
77
79
  */
78
- createCloneCell(args: AppCreateCloneCellRequest): Promise<CreateCloneCellResponse>;
80
+ createCloneCell(args: AppCreateCloneCellRequest): Promise<import("../admin/types.js").ClonedCell>;
79
81
  /**
80
82
  * Enable a disabled clone cell.
81
83
  *
82
84
  * @param args - Specify the clone cell to enable.
83
85
  * @returns The enabled clone cell.
84
86
  */
85
- enableCloneCell(args: AppEnableCloneCellRequest): Promise<EnableCloneCellResponse>;
87
+ enableCloneCell(args: AppEnableCloneCellRequest): Promise<import("../admin/types.js").ClonedCell>;
86
88
  /**
87
89
  * Disable an enabled clone cell.
88
90
  *
89
91
  * @param args - Specify the clone cell to disable.
90
92
  */
91
- disableCloneCell(args: AppDisableCloneCellRequest): Promise<DisableCloneCellResponse>;
93
+ disableCloneCell(args: AppDisableCloneCellRequest): Promise<void>;
92
94
  /**
93
95
  * Request network info about gossip status.
94
96
  * @param args - Specify the DNAs for which you want network info
95
97
  * @returns Network info for the specified DNAs
96
98
  */
97
99
  networkInfo(args: AppNetworkInfoRequest): Promise<NetworkInfoResponse>;
100
+ /**
101
+ * Get the state of a countersigning session.
102
+ */
103
+ getCountersigningSessionState(args: GetCountersigningSessionStateRequest): Promise<GetCountersigningSessionStateResponse>;
104
+ /**
105
+ * Abandon an unresolved countersigning session.
106
+ *
107
+ * If the current session has not been resolved automatically, it can be forcefully abandoned.
108
+ * A condition for this call to succeed is that at least one attempt has been made to resolve
109
+ * it automatically.
110
+ *
111
+ * # Returns
112
+ *
113
+ * [`AppResponse::CountersigningSessionAbandoned`]
114
+ *
115
+ * The session is marked for abandoning and the countersigning workflow was triggered. The session
116
+ * has not been abandoned yet.
117
+ *
118
+ * Upon successful abandoning the system signal [`SystemSignal::AbandonedCountersigning`] will
119
+ * be emitted and the session removed from state, so that [`AppRequest::GetCountersigningSessionState`]
120
+ * would return `None`.
121
+ *
122
+ * In the countersigning workflow it will first be attempted to resolve the session with incoming
123
+ * signatures of the countersigned entries, before force-abandoning the session. In a very rare event
124
+ * it could happen that in just the moment where the [`AppRequest::AbandonCountersigningSession`]
125
+ * is made, signatures for this session come in. If they are valid, the session will be resolved and
126
+ * published as usual. Should they be invalid, however, the flag to abandon the session is erased.
127
+ * In such cases this request can be retried until the session has been abandoned successfully.
128
+ *
129
+ * # Errors
130
+ *
131
+ * [`CountersigningError::WorkspaceDoesNotExist`] likely indicates that an invalid cell id was
132
+ * passed in to the call.
133
+ *
134
+ * [`CountersigningError::SessionNotFound`] when no ongoing session could be found for the provided
135
+ * cell id.
136
+ *
137
+ * [`CountersigningError::SessionNotUnresolved`] when an attempt to resolve the session
138
+ * automatically has not been made.
139
+ */
140
+ abandonCountersigningSession(args: AbandonCountersigningSessionStateRequest): Promise<null>;
141
+ /**
142
+ * Publish an unresolved countersigning session.
143
+ *
144
+ * If the current session has not been resolved automatically, it can be forcefully published.
145
+ * A condition for this call to succeed is that at least one attempt has been made to resolve
146
+ * it automatically.
147
+ *
148
+ * # Returns
149
+ *
150
+ * [`AppResponse::PublishCountersigningSessionTriggered`]
151
+ *
152
+ * The session is marked for publishing and the countersigning workflow was triggered. The session
153
+ * has not been published yet.
154
+ *
155
+ * Upon successful publishing the system signal [`SystemSignal::SuccessfulCountersigning`] will
156
+ * be emitted and the session removed from state, so that [`AppRequest::GetCountersigningSessionState`]
157
+ * would return `None`.
158
+ *
159
+ * In the countersigning workflow it will first be attempted to resolve the session with incoming
160
+ * signatures of the countersigned entries, before force-publishing the session. In a very rare event
161
+ * it could happen that in just the moment where the [`AppRequest::PublishCountersigningSession`]
162
+ * is made, signatures for this session come in. If they are valid, the session will be resolved and
163
+ * published as usual. Should they be invalid, however, the flag to publish the session is erased.
164
+ * In such cases this request can be retried until the session has been published successfully.
165
+ *
166
+ * # Errors
167
+ *
168
+ * [`CountersigningError::WorkspaceDoesNotExist`] likely indicates that an invalid cell id was
169
+ * passed in to the call.
170
+ *
171
+ * [`CountersigningError::SessionNotFound`] when no ongoing session could be found for the provided
172
+ * cell id.
173
+ *
174
+ * [`CountersigningError::SessionNotUnresolved`] when an attempt to resolve the session
175
+ * automatically has not been made.
176
+ */
177
+ publishCountersigningSession(args: PublishCountersigningSessionStateRequest): Promise<null>;
98
178
  /**
99
179
  * Register an event listener for signals.
100
180
  *
@@ -6,9 +6,9 @@ import { getHostZomeCallSigner, getLauncherEnvironment, } from "../../environmen
6
6
  import { decode, encode } from "@msgpack/msgpack";
7
7
  import { getNonceExpiration, getSigningCredentials, randomNonce, } from "../zome-call-signing.js";
8
8
  import { encodeHashToBase64 } from "../../utils/index.js";
9
- import { hashZomeCall } from "@holochain/serialization";
10
9
  import _sodium from "libsodium-wrappers";
11
10
  import { WsClient } from "../client.js";
11
+ import { sha512 } from "js-sha512";
12
12
  /**
13
13
  * A class to establish a websocket connection to an App interface, for a
14
14
  * specific agent and app.
@@ -22,7 +22,6 @@ export class AppWebsocket {
22
22
  defaultTimeout;
23
23
  emitter;
24
24
  callZomeTransform;
25
- appAuthenticationToken;
26
25
  cachedAppInfo;
27
26
  appInfoRequester;
28
27
  callZomeRequester;
@@ -32,13 +31,15 @@ export class AppWebsocket {
32
31
  enableCloneCellRequester;
33
32
  disableCloneCellRequester;
34
33
  networkInfoRequester;
35
- constructor(client, appInfo, token, callZomeTransform, defaultTimeout) {
34
+ getCountersigningSessionStateRequester;
35
+ abandonCountersigningSessionRequester;
36
+ publishCountersigningSessionRequester;
37
+ constructor(client, appInfo, callZomeTransform, defaultTimeout) {
36
38
  this.client = client;
37
39
  this.myPubKey = appInfo.agent_pub_key;
38
40
  this.installedAppId = appInfo.installed_app_id;
39
41
  this.defaultTimeout = defaultTimeout ?? DEFAULT_TIMEOUT;
40
42
  this.callZomeTransform = callZomeTransform ?? defaultCallZomeTransform;
41
- this.appAuthenticationToken = token;
42
43
  this.emitter = new Emittery();
43
44
  this.cachedAppInfo = appInfo;
44
45
  this.appInfoRequester = AppWebsocket.requester(this.client, "app_info", this.defaultTimeout);
@@ -49,6 +50,9 @@ export class AppWebsocket {
49
50
  this.enableCloneCellRequester = AppWebsocket.requester(this.client, "enable_clone_cell", this.defaultTimeout);
50
51
  this.disableCloneCellRequester = AppWebsocket.requester(this.client, "disable_clone_cell", this.defaultTimeout);
51
52
  this.networkInfoRequester = AppWebsocket.requester(this.client, "network_info", this.defaultTimeout);
53
+ this.getCountersigningSessionStateRequester = AppWebsocket.requester(this.client, "get_countersigning_session_state", this.defaultTimeout);
54
+ this.abandonCountersigningSessionRequester = AppWebsocket.requester(this.client, "abandon_countersigning_session", this.defaultTimeout);
55
+ this.publishCountersigningSessionRequester = AppWebsocket.requester(this.client, "publish_countersigning_session", this.defaultTimeout);
52
56
  // Ensure all super methods are bound to this instance because Emittery relies on `this` being the instance.
53
57
  // Please retain until the upstream is fixed https://github.com/sindresorhus/emittery/issues/86.
54
58
  Object.getOwnPropertyNames(Emittery.prototype).forEach((name) => {
@@ -87,7 +91,7 @@ export class AppWebsocket {
87
91
  if (!appInfo) {
88
92
  throw new HolochainError("AppNotFound", `The app your connection token was issued for was not found. The app needs to be installed and enabled.`);
89
93
  }
90
- return new AppWebsocket(client, appInfo, token, options.callZomeTransform, options.defaultTimeout);
94
+ return new AppWebsocket(client, appInfo, options.callZomeTransform, options.defaultTimeout);
91
95
  }
92
96
  /**
93
97
  * Request the app's info, including all cell infos.
@@ -220,6 +224,90 @@ export class AppWebsocket {
220
224
  agent_pub_key: this.myPubKey,
221
225
  });
222
226
  }
227
+ /**
228
+ * Get the state of a countersigning session.
229
+ */
230
+ async getCountersigningSessionState(args) {
231
+ return this.getCountersigningSessionStateRequester(args);
232
+ }
233
+ /**
234
+ * Abandon an unresolved countersigning session.
235
+ *
236
+ * If the current session has not been resolved automatically, it can be forcefully abandoned.
237
+ * A condition for this call to succeed is that at least one attempt has been made to resolve
238
+ * it automatically.
239
+ *
240
+ * # Returns
241
+ *
242
+ * [`AppResponse::CountersigningSessionAbandoned`]
243
+ *
244
+ * The session is marked for abandoning and the countersigning workflow was triggered. The session
245
+ * has not been abandoned yet.
246
+ *
247
+ * Upon successful abandoning the system signal [`SystemSignal::AbandonedCountersigning`] will
248
+ * be emitted and the session removed from state, so that [`AppRequest::GetCountersigningSessionState`]
249
+ * would return `None`.
250
+ *
251
+ * In the countersigning workflow it will first be attempted to resolve the session with incoming
252
+ * signatures of the countersigned entries, before force-abandoning the session. In a very rare event
253
+ * it could happen that in just the moment where the [`AppRequest::AbandonCountersigningSession`]
254
+ * is made, signatures for this session come in. If they are valid, the session will be resolved and
255
+ * published as usual. Should they be invalid, however, the flag to abandon the session is erased.
256
+ * In such cases this request can be retried until the session has been abandoned successfully.
257
+ *
258
+ * # Errors
259
+ *
260
+ * [`CountersigningError::WorkspaceDoesNotExist`] likely indicates that an invalid cell id was
261
+ * passed in to the call.
262
+ *
263
+ * [`CountersigningError::SessionNotFound`] when no ongoing session could be found for the provided
264
+ * cell id.
265
+ *
266
+ * [`CountersigningError::SessionNotUnresolved`] when an attempt to resolve the session
267
+ * automatically has not been made.
268
+ */
269
+ async abandonCountersigningSession(args) {
270
+ return this.abandonCountersigningSessionRequester(args);
271
+ }
272
+ /**
273
+ * Publish an unresolved countersigning session.
274
+ *
275
+ * If the current session has not been resolved automatically, it can be forcefully published.
276
+ * A condition for this call to succeed is that at least one attempt has been made to resolve
277
+ * it automatically.
278
+ *
279
+ * # Returns
280
+ *
281
+ * [`AppResponse::PublishCountersigningSessionTriggered`]
282
+ *
283
+ * The session is marked for publishing and the countersigning workflow was triggered. The session
284
+ * has not been published yet.
285
+ *
286
+ * Upon successful publishing the system signal [`SystemSignal::SuccessfulCountersigning`] will
287
+ * be emitted and the session removed from state, so that [`AppRequest::GetCountersigningSessionState`]
288
+ * would return `None`.
289
+ *
290
+ * In the countersigning workflow it will first be attempted to resolve the session with incoming
291
+ * signatures of the countersigned entries, before force-publishing the session. In a very rare event
292
+ * it could happen that in just the moment where the [`AppRequest::PublishCountersigningSession`]
293
+ * is made, signatures for this session come in. If they are valid, the session will be resolved and
294
+ * published as usual. Should they be invalid, however, the flag to publish the session is erased.
295
+ * In such cases this request can be retried until the session has been published successfully.
296
+ *
297
+ * # Errors
298
+ *
299
+ * [`CountersigningError::WorkspaceDoesNotExist`] likely indicates that an invalid cell id was
300
+ * passed in to the call.
301
+ *
302
+ * [`CountersigningError::SessionNotFound`] when no ongoing session could be found for the provided
303
+ * cell id.
304
+ *
305
+ * [`CountersigningError::SessionNotUnresolved`] when an attempt to resolve the session
306
+ * automatically has not been made.
307
+ */
308
+ async publishCountersigningSession(args) {
309
+ return this.publishCountersigningSessionRequester(args);
310
+ }
223
311
  /**
224
312
  * Register an event listener for signals.
225
313
  *
@@ -249,8 +337,6 @@ const defaultCallZomeTransform = {
249
337
  },
250
338
  output: (response) => decode(response),
251
339
  };
252
- const isSameCell = (cellId1, cellId2) => cellId1[0].every((byte, index) => byte === cellId2[0][index]) &&
253
- cellId1[1].every((byte, index) => byte === cellId2[1][index]);
254
340
  /**
255
341
  * @public
256
342
  */
@@ -269,14 +355,15 @@ export const signZomeCall = async (request) => {
269
355
  nonce: await randomNonce(),
270
356
  expires_at: getNonceExpiration(),
271
357
  };
272
- const hashedZomeCall = await hashZomeCall(unsignedZomeCallPayload);
358
+ const bytes = encode(unsignedZomeCallPayload);
359
+ const bytesHash = new Uint8Array(sha512.array(bytes));
273
360
  await _sodium.ready;
274
361
  const sodium = _sodium;
275
362
  const signature = sodium
276
- .crypto_sign(hashedZomeCall, signingCredentialsForCell.keyPair.privateKey)
363
+ .crypto_sign(bytesHash, signingCredentialsForCell.keyPair.privateKey)
277
364
  .subarray(0, sodium.crypto_sign_BYTES);
278
365
  const signedZomeCall = {
279
- ...unsignedZomeCallPayload,
366
+ bytes,
280
367
  signature,
281
368
  };
282
369
  return signedZomeCall;
@@ -1,4 +1,3 @@
1
- export { hashZomeCall } from "@holochain/serialization";
2
1
  export * from "./admin/index.js";
3
2
  export * from "./app/index.js";
4
3
  export { IsoWebSocket, WsClient, type AppAuthenticationRequest, } from "./client.js";
package/lib/api/index.js CHANGED
@@ -1,4 +1,3 @@
1
- export { hashZomeCall } from "@holochain/serialization";
2
1
  export * from "./admin/index.js";
3
2
  export * from "./app/index.js";
4
3
  export { IsoWebSocket, WsClient, } from "./client.js";
@@ -1,5 +1,5 @@
1
1
  import { AppAuthenticationToken, CallZomeRequest } from "../api/index.js";
2
- import { CallZomeRequestSigned, CallZomeRequestUnsigned } from "../api/index.js";
2
+ import { CallZomeRequestSigned, CallZomeRequestParams } from "../api/index.js";
3
3
  import { InstalledAppId } from "../types.js";
4
4
  export interface LauncherEnvironment {
5
5
  APP_INTERFACE_PORT?: number;
@@ -32,7 +32,7 @@ interface CallZomeRequestSignedElectron extends Omit<CallZomeRequestSigned, "cap
32
32
  nonce: Array<number>;
33
33
  expiresAt: number;
34
34
  }
35
- interface CallZomeRequestUnsignedElectron extends Omit<CallZomeRequestUnsigned, "cap_secret" | "cell_id" | "provenance" | "nonce" | "zome_name" | "fn_name" | "expires_at"> {
35
+ interface CallZomeRequestUnsignedElectron extends Omit<CallZomeRequestParams, "cap_secret" | "cell_id" | "provenance" | "nonce" | "zome_name" | "fn_name" | "expires_at"> {
36
36
  cellId: [Array<number>, Array<number>];
37
37
  provenance: Array<number>;
38
38
  zomeName: string;
@@ -1,6 +1,13 @@
1
1
  import { AgentPubKey, DnaHash, EntryHash, ActionHash, HoloHashed, Signature, Timestamp } from "../types.js";
2
2
  import { Entry, EntryType } from "./entry.js";
3
3
  import { LinkTag, LinkType, RateWeight } from "./link.js";
4
+ /**
5
+ * @public
6
+ */
7
+ export interface SignedAction {
8
+ data: Action;
9
+ signature: Signature;
10
+ }
4
11
  /**
5
12
  * @public
6
13
  */
@@ -5,7 +5,7 @@
5
5
  "toolPackages": [
6
6
  {
7
7
  "packageName": "@microsoft/api-extractor",
8
- "packageVersion": "7.47.9"
8
+ "packageVersion": "7.47.11"
9
9
  }
10
10
  ]
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@holochain/client",
3
- "version": "0.19.0-dev.1",
3
+ "version": "0.19.0-dev.3",
4
4
  "description": "A JavaScript client for the Holochain Conductor API",
5
5
  "author": "Holochain Foundation <info@holochain.org> (https://holochain.org)",
6
6
  "license": "CAL-1.0",
@@ -43,11 +43,11 @@
43
43
  },
44
44
  "dependencies": {
45
45
  "@bitgo/blake2b": "^3.2.4",
46
- "@holochain/serialization": "^0.1.0-beta-rc.3",
47
46
  "@msgpack/msgpack": "^2.8.0",
48
47
  "emittery": "^1.0.1",
49
48
  "isomorphic-ws": "^5.0.0",
50
49
  "js-base64": "^3.7.5",
50
+ "js-sha512": "^0.9.0",
51
51
  "libsodium-wrappers": "^0.7.13",
52
52
  "lodash-es": "^4.17.21",
53
53
  "ws": "^8.14.2"
@@ -72,4 +72,4 @@
72
72
  "tsx": "^4.7.2",
73
73
  "typescript": "^4.9.5"
74
74
  }
75
- }
75
+ }