zkcloudworker 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. package/lib/ts/src/api/api.d.ts +4 -0
  2. package/lib/ts/src/api/api.js +44 -4
  3. package/lib/ts/src/cloud/job.d.ts +7 -0
  4. package/lib/ts/src/config.d.ts +1 -0
  5. package/lib/ts/src/config.js +1 -0
  6. package/lib/ts/src/encryption/encryption.d.ts +12 -0
  7. package/lib/ts/src/encryption/encryption.js +41 -0
  8. package/lib/ts/src/encryption/messages.d.ts +2 -0
  9. package/lib/ts/src/encryption/messages.js +38 -0
  10. package/lib/ts/src/encryption/nats-client.d.ts +1 -0
  11. package/lib/ts/src/encryption/nats-client.js +85 -0
  12. package/lib/ts/src/index.d.ts +3 -0
  13. package/lib/ts/src/index.js +3 -0
  14. package/lib/ts/tsconfig.tsbuildinfo +1 -1
  15. package/lib/web/src/api/api.d.ts +4 -0
  16. package/lib/web/src/api/api.js +47 -6
  17. package/lib/web/src/api/api.js.map +1 -1
  18. package/lib/web/src/cloud/job.d.ts +7 -0
  19. package/lib/web/src/config.d.ts +1 -0
  20. package/lib/web/src/config.js +1 -0
  21. package/lib/web/src/config.js.map +1 -1
  22. package/lib/web/src/encryption/encryption.d.ts +12 -0
  23. package/lib/web/src/encryption/encryption.js +41 -0
  24. package/lib/web/src/encryption/encryption.js.map +1 -0
  25. package/lib/web/src/encryption/messages.d.ts +2 -0
  26. package/lib/web/src/encryption/messages.js +36 -0
  27. package/lib/web/src/encryption/messages.js.map +1 -0
  28. package/lib/web/src/encryption/nats-client.d.ts +1 -0
  29. package/lib/web/src/encryption/nats-client.js +95 -0
  30. package/lib/web/src/encryption/nats-client.js.map +1 -0
  31. package/lib/web/src/index.d.ts +3 -0
  32. package/lib/web/src/index.js +3 -0
  33. package/lib/web/src/index.js.map +1 -1
  34. package/lib/web/tsconfig.web.tsbuildinfo +1 -1
  35. package/package.json +1 -1
@@ -108,6 +108,7 @@ export declare class zkCloudWorkerClient {
108
108
  * Gets the result of the job using serverless api call
109
109
  * @param data the data for the jobResult call
110
110
  * @param data.jobId the jobId of the job
111
+ * @param data.includeLogs include logs in the result, default is false
111
112
  * @returns { success: boolean, error?: string, result?: any }
112
113
  * where result is the result of the job
113
114
  * if the job is not finished yet, the result will be undefined
@@ -117,6 +118,7 @@ export declare class zkCloudWorkerClient {
117
118
  */
118
119
  jobResult(data: {
119
120
  jobId: string;
121
+ includeLogs?: boolean;
120
122
  }): Promise<{
121
123
  success: boolean;
122
124
  error?: string;
@@ -169,6 +171,7 @@ export declare class zkCloudWorkerClient {
169
171
  * @param data.maxAttempts the maximum number of attempts, default is 360 (2 hours)
170
172
  * @param data.interval the interval between attempts, default is 20000 (20 seconds)
171
173
  * @param data.maxErrors the maximum number of network errors, default is 10
174
+ * @param data.printLogs print logs, default is true
172
175
  * @returns { success: boolean, error?: string, result?: any }
173
176
  * where result is the result of the job
174
177
  */
@@ -177,6 +180,7 @@ export declare class zkCloudWorkerClient {
177
180
  maxAttempts?: number;
178
181
  interval?: number;
179
182
  maxErrors?: number;
183
+ printLogs?: boolean;
180
184
  }): Promise<{
181
185
  success: boolean;
182
186
  error?: string;
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.zkCloudWorkerClient = void 0;
7
7
  const axios_1 = __importDefault(require("axios"));
8
+ const chalk_1 = __importDefault(require("chalk"));
8
9
  const mina_1 = require("../mina");
9
10
  const local_1 = require("../cloud/local");
10
11
  const config_1 = __importDefault(require("../config"));
@@ -85,13 +86,23 @@ class zkCloudWorkerClient {
85
86
  success: false,
86
87
  error: result.error,
87
88
  };
88
- else
89
+ else if (result.success === false || result.data?.success === false)
90
+ return {
91
+ success: false,
92
+ error: result.error ?? result.data?.error ?? "execute call failed",
93
+ };
94
+ else if (result.success === true && result.data?.success === true)
89
95
  return {
90
96
  success: result.success,
91
- jobId: data.mode === "sync" ? undefined : result.data,
97
+ jobId: data.mode === "sync" ? undefined : result.data.jobId,
92
98
  result: data.mode === "sync" ? result.data : undefined,
93
99
  error: result.error,
94
100
  };
101
+ else
102
+ return {
103
+ success: false,
104
+ error: "execute call error",
105
+ };
95
106
  }
96
107
  /**
97
108
  * Starts a new job for the function call using serverless api call
@@ -124,6 +135,7 @@ class zkCloudWorkerClient {
124
135
  * Gets the result of the job using serverless api call
125
136
  * @param data the data for the jobResult call
126
137
  * @param data.jobId the jobId of the job
138
+ * @param data.includeLogs include logs in the result, default is false
127
139
  * @returns { success: boolean, error?: string, result?: any }
128
140
  * where result is the result of the job
129
141
  * if the job is not finished yet, the result will be undefined
@@ -225,18 +237,39 @@ class zkCloudWorkerClient {
225
237
  * @param data.maxAttempts the maximum number of attempts, default is 360 (2 hours)
226
238
  * @param data.interval the interval between attempts, default is 20000 (20 seconds)
227
239
  * @param data.maxErrors the maximum number of network errors, default is 10
240
+ * @param data.printLogs print logs, default is true
228
241
  * @returns { success: boolean, error?: string, result?: any }
229
242
  * where result is the result of the job
230
243
  */
231
244
  async waitForJobResult(data) {
232
245
  const maxAttempts = data?.maxAttempts ?? 360; // 2 hours
233
- const interval = data?.interval ?? 20000;
246
+ const interval = data?.interval ?? 10000;
234
247
  const maxErrors = data?.maxErrors ?? 10;
235
248
  const errorDelay = 30000; // 30 seconds
249
+ const printedLogs = [];
250
+ const printLogs = data.printLogs ?? true;
251
+ function print(logs) {
252
+ logs.forEach((log) => {
253
+ if (printedLogs.includes(log) === false) {
254
+ printedLogs.push(log);
255
+ // replace all occurrences of "error" with red color
256
+ const text = log.replace(/error/gi, (matched) => chalk_1.default.red(matched));
257
+ console.log(text);
258
+ }
259
+ });
260
+ }
236
261
  let attempts = 0;
237
262
  let errors = 0;
238
263
  while (attempts < maxAttempts) {
239
- const result = await this.apiHub("jobResult", data);
264
+ const result = await this.apiHub("jobResult", {
265
+ jobId: data.jobId,
266
+ includeLogs: printLogs,
267
+ });
268
+ if (printLogs === true &&
269
+ result?.data?.logs !== undefined &&
270
+ result?.data?.logs !== null &&
271
+ Array.isArray(result.data.logs) === true)
272
+ print(result.data.logs);
240
273
  if (result.success === false) {
241
274
  errors++;
242
275
  if (errors > maxErrors) {
@@ -262,6 +295,13 @@ class zkCloudWorkerClient {
262
295
  result: result.data,
263
296
  };
264
297
  }
298
+ else if (result.data?.jobStatus === "failed") {
299
+ return {
300
+ success: false,
301
+ error: "Job failed",
302
+ result: result.data,
303
+ };
304
+ }
265
305
  await (0, mina_1.sleep)(interval);
266
306
  }
267
307
  attempts++;
@@ -1,5 +1,10 @@
1
1
  import { blockchain } from "../networks";
2
2
  export type JobStatus = "created" | "started" | "finished" | "failed" | "used";
3
+ export interface LogStream {
4
+ logGroupName: string;
5
+ logStreamName: string;
6
+ awsRequestId: string;
7
+ }
3
8
  export interface JobData {
4
9
  id: string;
5
10
  jobId: string;
@@ -29,4 +34,6 @@ export interface JobData {
29
34
  jobStatus: JobStatus;
30
35
  maxAttempts: number;
31
36
  result?: string;
37
+ logStreams?: LogStream[];
38
+ logs?: string[];
32
39
  }
@@ -2,5 +2,6 @@ declare const config: {
2
2
  MINAFEE: string;
3
3
  ZKCLOUDWORKER_AUTH: string;
4
4
  ZKCLOUDWORKER_API: string;
5
+ NATS_SERVER: string;
5
6
  };
6
7
  export default config;
@@ -4,5 +4,6 @@ const config = {
4
4
  MINAFEE: "200000000",
5
5
  ZKCLOUDWORKER_AUTH: "M6t4jtbBAFFXhLERHQWyEB9JA9xi4cWqmYduaCXtbrFjb7yaY7TyaXDunKDJNiUTBEcyUomNXJgC",
6
6
  ZKCLOUDWORKER_API: "https://cuq99yahhi.execute-api.eu-west-1.amazonaws.com/dev/zkcloudworker",
7
+ NATS_SERVER: "nats.socialcap.dev:4222",
7
8
  };
8
9
  exports.default = config;
@@ -0,0 +1,12 @@
1
+ import { Field, Group } from "o1js";
2
+ export interface CipherTextObject {
3
+ cipherText: Field[];
4
+ publicKey: Group;
5
+ }
6
+ export declare class CipherText {
7
+ static stringify(cipherText: CipherTextObject): string;
8
+ static parse(jsonStr: string): CipherTextObject;
9
+ static encrypt(message: string, publicId: string): string;
10
+ static decrypt(cipherText: string, privateKey: string): string;
11
+ static initialize(): Promise<void>;
12
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CipherText = void 0;
4
+ const o1js_1 = require("o1js");
5
+ class CipherText {
6
+ static stringify(cipherText) {
7
+ return JSON.stringify(cipherText);
8
+ }
9
+ static parse(jsonStr) {
10
+ let obj = JSON.parse(jsonStr);
11
+ return {
12
+ publicKey: new o1js_1.Group(obj.publicKey),
13
+ cipherText: (obj.cipherText || []).map((t) => (0, o1js_1.Field)(t)),
14
+ };
15
+ }
16
+ static encrypt(message, publicId) {
17
+ try {
18
+ let fields = o1js_1.Encoding.stringToFields(message);
19
+ let encrypted = o1js_1.Encryption.encrypt(fields, o1js_1.PublicKey.fromBase58(publicId));
20
+ return CipherText.stringify(encrypted);
21
+ }
22
+ catch (err) {
23
+ throw Error(`Could not encrypt message='${message}' using key='${publicId}'.` +
24
+ ` Error ${err}`);
25
+ }
26
+ }
27
+ static decrypt(cipherText, privateKey) {
28
+ try {
29
+ let fields = o1js_1.Encryption.decrypt(CipherText.parse(cipherText), o1js_1.PrivateKey.fromBase58(privateKey));
30
+ let decrypted = o1js_1.Encoding.stringFromFields(fields);
31
+ return decrypted;
32
+ }
33
+ catch (err) {
34
+ throw Error(`Could not decrypt cipher='${cipherText}'.` + ` Error ${err}`);
35
+ }
36
+ }
37
+ static async initialize() {
38
+ await (0, o1js_1.initializeBindings)();
39
+ }
40
+ }
41
+ exports.CipherText = CipherText;
@@ -0,0 +1,2 @@
1
+ export declare function postReadyMessage(clientAddress: string, workerAddress: string): Promise<any>;
2
+ export declare function postDoneMessage(clientAddress: string, encrypted: string): Promise<any>;
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.postDoneMessage = exports.postReadyMessage = void 0;
7
+ const nats_1 = require("nats");
8
+ const config_1 = __importDefault(require("../config"));
9
+ const { NATS_SERVER } = config_1.default;
10
+ const codec = (0, nats_1.JSONCodec)();
11
+ async function postReadyMessage(clientAddress, workerAddress) {
12
+ // connect to the NATS server and send a 'ready' request
13
+ const nc = await (0, nats_1.connect)({ servers: NATS_SERVER });
14
+ const msg = await nc.request(`zkcw:${clientAddress}`, codec.encode({
15
+ post: "ready",
16
+ params: { key: workerAddress },
17
+ }));
18
+ const response = codec.decode(msg.data);
19
+ console.log("Response: ", response);
20
+ // disconnect and clean all pending
21
+ await nc.drain();
22
+ return response;
23
+ }
24
+ exports.postReadyMessage = postReadyMessage;
25
+ async function postDoneMessage(clientAddress, encrypted) {
26
+ // connect to the NATS server and send a 'ready' request
27
+ const nc = await (0, nats_1.connect)({ servers: NATS_SERVER });
28
+ const msg = await nc.request(`zkcw:${clientAddress}`, codec.encode({
29
+ post: "done",
30
+ params: { result: encrypted },
31
+ }));
32
+ const response = codec.decode(msg.data);
33
+ console.log("Response: ", response);
34
+ // disconnect and clean all pending
35
+ await nc.drain();
36
+ return response;
37
+ }
38
+ exports.postDoneMessage = postDoneMessage;
@@ -0,0 +1 @@
1
+ export declare function listen(subject: string): Promise<void>;
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listen = void 0;
7
+ const nats_1 = require("nats");
8
+ const o1js_1 = require("o1js");
9
+ const encryption_1 = require("./encryption");
10
+ const config_1 = __importDefault(require("../config"));
11
+ const { NATS_SERVER } = config_1.default;
12
+ async function listen(subject) {
13
+ // Create a JSON codec for encoding and decoding messages
14
+ const codec = (0, nats_1.JSONCodec)();
15
+ const connection = await (0, nats_1.connect)({ servers: NATS_SERVER });
16
+ // Subscribe to the subject
17
+ const subscription = connection.subscribe(subject);
18
+ console.log(`Subscribed to subject ${subject}`);
19
+ // Process messages received on the subscribed subject
20
+ (async () => {
21
+ // Error decoding message: Error: Could not encrypt message={}
22
+ // Error: Poseidon.Sponge(): bindings are not initialized, try calling `await initializeBindings()` first.
23
+ // This shouldn't have happened and indicates an internal bug.
24
+ await (0, o1js_1.initializeBindings)();
25
+ for await (const msg of subscription) {
26
+ try {
27
+ const data = codec.decode(msg.data);
28
+ //console.log(`Received message on subject ${subject}:`, data);
29
+ // Perform processing logic here
30
+ const { post, params } = data;
31
+ // console.log(`Post: `, post, params);
32
+ switch (post) {
33
+ case "ready":
34
+ {
35
+ // the workers announces it is ready
36
+ // and we receive the worker's publicKey
37
+ let workerKey = params.key || "";
38
+ console.log("Received 'ready' message from worker");
39
+ console.log("Worker publicKey: ", workerKey);
40
+ // we will use its key to encrypt the message
41
+ const encryptedPayload = encryption_1.CipherText.encrypt(JSON.stringify({
42
+ value: Math.ceil(Math.random() * 100).toString(),
43
+ }), workerKey);
44
+ console.log("Encrypted payload: ", encryptedPayload);
45
+ // we reply with the command we want the worker to execute
46
+ // and with the encrypted payload
47
+ msg.respond(codec.encode({
48
+ success: true,
49
+ data: {
50
+ command: "execute",
51
+ encrypted: encryptedPayload,
52
+ },
53
+ error: undefined,
54
+ }));
55
+ }
56
+ break;
57
+ case "done":
58
+ {
59
+ let result = params.result || "";
60
+ console.log("Received 'done' message from worker");
61
+ msg.respond(codec.encode({
62
+ success: true,
63
+ data: { status: "closed" },
64
+ error: undefined,
65
+ }));
66
+ // we want to insure that messages that are in flight
67
+ // get processed, so we are going to drain the
68
+ // connection. Drain is the same as close, but makes
69
+ // sure that all messages in flight get seen
70
+ // by the iterator. After calling drain on the connection
71
+ // the connection closes.
72
+ setTimeout(async () => {
73
+ await connection.drain();
74
+ }, 1000);
75
+ }
76
+ break;
77
+ }
78
+ }
79
+ catch (err) {
80
+ console.error("Error decoding message: ", err);
81
+ }
82
+ }
83
+ })();
84
+ }
85
+ exports.listen = listen;
@@ -8,3 +8,6 @@ export * from "./fee";
8
8
  export * from "./fetch";
9
9
  export * from "./networks";
10
10
  export * from "./api/client-api";
11
+ export * from "./encryption/encryption";
12
+ export * from "./encryption/nats-client";
13
+ export * from "./encryption/messages";
@@ -24,3 +24,6 @@ __exportStar(require("./fee"), exports);
24
24
  __exportStar(require("./fetch"), exports);
25
25
  __exportStar(require("./networks"), exports);
26
26
  __exportStar(require("./api/client-api"), exports);
27
+ __exportStar(require("./encryption/encryption"), exports);
28
+ __exportStar(require("./encryption/nats-client"), exports);
29
+ __exportStar(require("./encryption/messages"), exports);