@vlayer/sdk 0.1.0-nightly-20250127-96fc5df → 0.1.0-nightly-20250128-0a32cfc

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 (63) hide show
  1. package/dist/api/lib/client.d.ts.map +1 -1
  2. package/dist/api/lib/client.js.map +1 -1
  3. package/dist/api/prover.d.ts.map +1 -1
  4. package/dist/api/prover.js +1 -2
  5. package/dist/api/prover.js.map +1 -1
  6. package/dist/api/v_call.d.ts +1 -1
  7. package/dist/api/v_call.d.ts.map +1 -1
  8. package/dist/api/v_call.js +8 -2
  9. package/dist/api/v_call.js.map +1 -1
  10. package/dist/api/v_getProofReceipt.d.ts.map +1 -1
  11. package/dist/api/v_getProofReceipt.js.map +1 -1
  12. package/dist/api/webProof/createWebProofRequest.d.ts.map +1 -1
  13. package/dist/api/webProof/createWebProofRequest.js.map +1 -1
  14. package/package.json +20 -7
  15. package/src/api/email/dnsResolver.test.ts +19 -0
  16. package/src/api/email/dnsResolver.ts +87 -0
  17. package/src/api/email/parseEmail.test.ts +133 -0
  18. package/src/api/email/parseEmail.ts +55 -0
  19. package/src/api/email/preverify.test.ts +201 -0
  20. package/src/api/email/preverify.ts +70 -0
  21. package/src/api/email/testdata/test_email.txt +21 -0
  22. package/src/api/email/testdata/test_email_multiple_dkims.txt +28 -0
  23. package/src/api/email/testdata/test_email_subdomain.txt +21 -0
  24. package/src/api/email/testdata/test_email_unknown_domain.txt +21 -0
  25. package/src/api/lib/client.test.ts +261 -0
  26. package/src/api/lib/client.ts +191 -0
  27. package/src/api/lib/errors.ts +19 -0
  28. package/src/api/lib/types/ethereum.ts +45 -0
  29. package/src/api/lib/types/index.ts +3 -0
  30. package/src/api/lib/types/viem.ts +26 -0
  31. package/src/api/lib/types/vlayer.ts +156 -0
  32. package/src/api/lib/types/webProofProvider.ts +68 -0
  33. package/src/api/prover.ts +120 -0
  34. package/src/api/utils/prefixAllButNthSubstring.test.ts +24 -0
  35. package/src/api/utils/prefixAllButNthSubstring.ts +13 -0
  36. package/src/api/utils/versions.test.ts +52 -0
  37. package/src/api/utils/versions.ts +31 -0
  38. package/src/api/v_call.ts +58 -0
  39. package/src/api/v_getProofReceipt.ts +48 -0
  40. package/src/api/v_versions.ts +68 -0
  41. package/src/api/webProof/createWebProofRequest.ts +15 -0
  42. package/src/api/webProof/index.ts +3 -0
  43. package/src/api/webProof/providers/extension.test.ts +122 -0
  44. package/src/api/webProof/providers/extension.ts +197 -0
  45. package/src/api/webProof/providers/index.ts +1 -0
  46. package/src/api/webProof/steps/expectUrl.ts +12 -0
  47. package/src/api/webProof/steps/index.ts +11 -0
  48. package/src/api/webProof/steps/notarize.ts +20 -0
  49. package/src/api/webProof/steps/startPage.ts +12 -0
  50. package/src/config/createContext.ts +69 -0
  51. package/src/config/deploy.ts +108 -0
  52. package/src/config/getChainConfirmations.ts +6 -0
  53. package/src/config/getConfig.ts +71 -0
  54. package/src/config/index.ts +5 -0
  55. package/src/config/types.ts +26 -0
  56. package/src/config/writeEnvVariables.ts +28 -0
  57. package/src/index.ts +7 -0
  58. package/src/testHelpers/readFile.ts +3 -0
  59. package/src/web-proof-commons/index.ts +3 -0
  60. package/src/web-proof-commons/types/message.ts +176 -0
  61. package/src/web-proof-commons/types/redaction.test.ts +97 -0
  62. package/src/web-proof-commons/types/redaction.ts +201 -0
  63. package/src/web-proof-commons/utils.ts +11 -0
@@ -0,0 +1,201 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { readFile } from "testHelpers/readFile";
3
+ import { findIndicesOfMatchingDomains, preverifyEmail } from "./preverify";
4
+
5
+ const rawEmail = readFile("./src/api/email/testdata/test_email.txt");
6
+
7
+ describe("Preverify email: integration", () => {
8
+ test("adds dns record to email mime", async () => {
9
+ const preverifiedEmail = await preverifyEmail(
10
+ rawEmail,
11
+ "https://dns.google/resolve",
12
+ );
13
+ expect(preverifiedEmail.email).toBe(rawEmail);
14
+ expect(preverifiedEmail.dnsRecord).toMatchObject({
15
+ name: "20230601._domainkey.google.com.",
16
+ recordType: 16,
17
+ ttl: expect.any(BigInt), // eslint-disable-line @typescript-eslint/no-unsafe-assignment
18
+ data: expect.stringContaining("v=DKIM1; k=rsa; p="), // eslint-disable-line @typescript-eslint/no-unsafe-assignment
19
+ });
20
+ expect(preverifiedEmail.dnsRecord.data).toContain("v=DKIM1; k=rsa; p=");
21
+ });
22
+
23
+ test("throws error if DKIM not found", async () => {
24
+ const emailWithNoDkimHeader = 'From: "Alice"\n\nBody';
25
+ await expect(
26
+ preverifyEmail(emailWithNoDkimHeader, "https://dns.google/resolve"),
27
+ ).rejects.toThrow("No DKIM header found");
28
+ });
29
+
30
+ test("throws error if DNS could not be resolved", async () => {
31
+ const emailWithNoDkimHeader = readFile(
32
+ "./src/api/email/testdata/test_email_unknown_domain.txt",
33
+ );
34
+ await expect(
35
+ preverifyEmail(emailWithNoDkimHeader, "https://dns.google/resolve"),
36
+ ).rejects.toThrow();
37
+ });
38
+
39
+ describe("multiple DKIM headers", () => {
40
+ function addDkimWithDomain(domain: string, email: string) {
41
+ return `DKIM-Signature: v=1; a=rsa-sha256; d=${domain};
42
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
43
+ ${email}`;
44
+ }
45
+
46
+ function addFakeDkimWithDomain(domain: string, email: string) {
47
+ return `X-${addDkimWithDomain(domain, email)}`;
48
+ }
49
+
50
+ test("looks for DKIM header with the domain matching the sender and removes all other DKIM headers", async () => {
51
+ const emailWithAddedHeaders = ["example.com", "hello.kitty"].reduce(
52
+ (email, domain) => addDkimWithDomain(domain, email),
53
+ rawEmail,
54
+ );
55
+ const email = await preverifyEmail(
56
+ emailWithAddedHeaders,
57
+ "https://dns.google/resolve",
58
+ );
59
+ expect(
60
+ email.email
61
+ .startsWith(`X-DKIM-Signature: v=1; a=rsa-sha256; d=hello.kitty;
62
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
63
+ X-DKIM-Signature: v=1; a=rsa-sha256; d=example.com;
64
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
65
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;\r
66
+ c=simple/simple; d=google.com;`),
67
+ ).toBeTruthy();
68
+ expect(email.dnsRecord.data).toEqual(
69
+ "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4zd3nfUoLHWFbfoPZzAb8bvjsFIIFsNypweLuPe4M+vAP1YxObFxRnpvLYz7Z+bORKLber5aGmgFF9iaufsH1z0+aw8Qex7uDaafzWoJOM/6lAS5iI0JggZiUkqNpRQLL7H6E7HcvOMC61nJcO4r0PwLDZKwEaCs8gUHiqRn/SS3wqEZX29v/VOUVcI4BjaOzOCLaz7V8Bkwmj4Rqq4kaLQQrbfpjas1naScHTAmzULj0Rdp+L1vVyGitm+dd460PcTIG3Pn+FYrgQQo2fvnTcGiFFuMa8cpxgfH3rJztf1YFehLWwJWgeXTriuIyuxUabGdRQu7vh7GrObTsHmIHwIDAQAB",
70
+ );
71
+ });
72
+
73
+ test("throws error if no DNS record domain matches the sender", async () => {
74
+ const emailWithOneDkimHeader = readFile(
75
+ "./src/api/email/testdata/test_email_unknown_domain.txt",
76
+ );
77
+ const emailWithAddedHeaders = addDkimWithDomain(
78
+ "otherdomain.com",
79
+ emailWithOneDkimHeader,
80
+ );
81
+ await expect(
82
+ preverifyEmail(emailWithAddedHeaders, "https://dns.google/resolve"),
83
+ ).rejects.toThrow("Found 0 DKIM headers matching the sender domain");
84
+ });
85
+
86
+ test("removes signatures from subdomain signers", async () => {
87
+ const email = await preverifyEmail(
88
+ addDkimWithDomain("subdomain.google.com", rawEmail),
89
+ "https://dns.google/resolve",
90
+ );
91
+ expect(
92
+ email.email
93
+ .startsWith(`X-DKIM-Signature: v=1; a=rsa-sha256; d=subdomain.google.com;
94
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
95
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;\r
96
+ c=simple/simple; d=google.com;`),
97
+ ).toBeTruthy();
98
+ });
99
+
100
+ test("removes signatures with mismatching subdomains", async () => {
101
+ const emailWithAddedHeaders = addDkimWithDomain(
102
+ "subdomain.google.com",
103
+ readFile("./src/api/email/testdata/test_email_subdomain.txt"),
104
+ );
105
+ await expect(
106
+ preverifyEmail(emailWithAddedHeaders, "https://dns.google/resolve"),
107
+ ).rejects.toThrow("Found 0 DKIM headers matching the sender domain");
108
+ });
109
+
110
+ test("throws error if multiple DNS record domains match the sender", async () => {
111
+ let emailWithAddedHeaders = addDkimWithDomain("google.com", rawEmail);
112
+ emailWithAddedHeaders = addDkimWithDomain(
113
+ "google.com",
114
+ emailWithAddedHeaders,
115
+ );
116
+ await expect(
117
+ preverifyEmail(emailWithAddedHeaders, "https://dns.google/resolve"),
118
+ ).rejects.toThrow("Found 3 DKIM headers matching the sender domain");
119
+ });
120
+
121
+ test("ignores x-dkim-signature headers", async () => {
122
+ const emailWithPrefixedDkim = addFakeDkimWithDomain(
123
+ "example.com",
124
+ addFakeDkimWithDomain("example.com", rawEmail),
125
+ );
126
+ const email = await preverifyEmail(
127
+ emailWithPrefixedDkim,
128
+ "https://dns.google/resolve",
129
+ );
130
+ expect(
131
+ email.email
132
+ .startsWith(`X-DKIM-Signature: v=1; a=rsa-sha256; d=example.com;
133
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
134
+ X-DKIM-Signature: v=1; a=rsa-sha256; d=example.com;
135
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
136
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;\r
137
+ c=simple/simple; d=google.com;`),
138
+ ).toBeTruthy();
139
+ });
140
+
141
+ test("ignores dkim-signature somewhere inside a header", async () => {
142
+ const headerWithDkim = `WTF-IS-THIS-HEADER: DKIM-SIGNATURE;`;
143
+ const emailWithDkimInHeader = `${headerWithDkim}\n${addDkimWithDomain("example.com", rawEmail)}`;
144
+ const email = await preverifyEmail(
145
+ emailWithDkimInHeader,
146
+ "https://dns.google/resolve",
147
+ );
148
+ expect(
149
+ email.email.startsWith(`WTF-IS-THIS-HEADER: DKIM-SIGNATURE;
150
+ X-DKIM-Signature: v=1; a=rsa-sha256; d=example.com;
151
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
152
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;\r
153
+ c=simple/simple; d=google.com;`),
154
+ ).toBeTruthy();
155
+ });
156
+
157
+ test("ignores dkim-signature somewhere inside a body", async () => {
158
+ const emailWithAddedDkim = addDkimWithDomain("example.com", rawEmail);
159
+ const emailWithDkimsInBody = `${emailWithAddedDkim}\ndkim-signature dkim-signature\r\ndkim-signature`;
160
+ const email = await preverifyEmail(
161
+ emailWithDkimsInBody,
162
+ "https://dns.google/resolve",
163
+ );
164
+ expect(
165
+ email.email
166
+ .startsWith(`X-DKIM-Signature: v=1; a=rsa-sha256; d=example.com;
167
+ s=selector; c=relaxed/relaxed; q=dns/txt; bh=; h=From:Subject:Date:To; b=
168
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;\r
169
+ c=simple/simple; d=google.com;`),
170
+ ).toBeTruthy();
171
+ expect(
172
+ email.email.endsWith(
173
+ `\ndkim-signature dkim-signature\r\ndkim-signature`,
174
+ ),
175
+ ).toBeTruthy();
176
+ });
177
+ });
178
+ });
179
+
180
+ describe("findIndicesOfMatchingDomains", () => {
181
+ test("returns indices of matching domains", () => {
182
+ const signers = [
183
+ { domain: "example.com", selector: "selector1" },
184
+ { domain: "other.other", selector: "selector2" },
185
+ { domain: "example.com", selector: "selector3" },
186
+ ];
187
+ expect(
188
+ findIndicesOfMatchingDomains(signers, "some@example.com"),
189
+ ).toStrictEqual([0, 2]);
190
+ });
191
+
192
+ test("returns empty array if no matching domains", () => {
193
+ const signers = [
194
+ { domain: "example.com", selector: "selector1" },
195
+ { domain: "example.org", selector: "selector2" },
196
+ ];
197
+ expect(findIndicesOfMatchingDomains(signers, "other.other")).toStrictEqual(
198
+ [],
199
+ );
200
+ });
201
+ });
@@ -0,0 +1,70 @@
1
+ import {
2
+ type DkimDomainSelector,
3
+ getDkimSigners,
4
+ parseEmail,
5
+ } from "./parseEmail";
6
+ import { DnsResolver } from "./dnsResolver";
7
+ import { prefixAllButNthSubstring } from "../utils/prefixAllButNthSubstring";
8
+
9
+ export function findIndicesOfMatchingDomains(
10
+ signers: DkimDomainSelector[],
11
+ expectedOrigin: string,
12
+ ) {
13
+ return signers
14
+ .map(({ domain }) => expectedOrigin.endsWith(`@${domain}`))
15
+ .map((isMatch, index) => (isMatch ? index : -1))
16
+ .filter((index) => index !== -1);
17
+ }
18
+
19
+ function requireSameOrigin(
20
+ mimeEmail: string,
21
+ signers: DkimDomainSelector[],
22
+ fromAddress: string,
23
+ ) {
24
+ const matchingIndices = findIndicesOfMatchingDomains(signers, fromAddress);
25
+
26
+ if (matchingIndices.length != 1) {
27
+ throw new Error(
28
+ `Found ${matchingIndices.length} DKIM headers matching the sender domain`,
29
+ );
30
+ }
31
+
32
+ const [matchingIndex] = matchingIndices;
33
+
34
+ return [
35
+ prefixAllButNthSubstring(
36
+ mimeEmail,
37
+ /^\s*dkim-signature/gim,
38
+ signers.length,
39
+ matchingIndex,
40
+ ),
41
+ [signers[matchingIndex]] as DkimDomainSelector[],
42
+ ] as const;
43
+ }
44
+
45
+ export async function preverifyEmail(
46
+ mimeEmail: string,
47
+ dnsResolverUrl: string,
48
+ ) {
49
+ const parsedEmail = await parseEmail(mimeEmail);
50
+ let signers = getDkimSigners(parsedEmail);
51
+ const fromAddress = parsedEmail.from.address;
52
+
53
+ if (!fromAddress) {
54
+ throw new Error("No from address found");
55
+ }
56
+ if (signers.length === 0) {
57
+ throw new Error("No DKIM header found");
58
+ }
59
+ [mimeEmail, signers] = requireSameOrigin(mimeEmail, signers, fromAddress);
60
+
61
+ const [{ domain, selector }] = signers;
62
+ const resolver = new DnsResolver(dnsResolverUrl);
63
+
64
+ const dnsResponse = await resolver.resolveDkimDns(selector, domain);
65
+
66
+ return {
67
+ email: mimeEmail,
68
+ ...dnsResponse,
69
+ };
70
+ }
@@ -0,0 +1,21 @@
1
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
2
+ c=simple/simple; d=google.com;
3
+ h=Received:From:To:Subject:Date:Message-ID; i=joe@google.com;
4
+ s=20230601; t=1615825284; v=1;
5
+ b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
6
+ k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
7
+ s4wwFRRKz/1bksZGSjD8uuSU=
8
+ Received: from client1.football.example.com [192.0.2.1]
9
+ by submitserver.example.com with SUBMISSION;
10
+ Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
11
+ From: Joe SixPack <joe@google.com>
12
+ To: Suzie Q <suzie@shopping.example.net>
13
+ Subject: Is dinner ready?
14
+ Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
15
+ Message-ID: <20030712040037.46341.5F8J@football.example.com>
16
+
17
+ Hi.
18
+
19
+ We lost the game. Are you hungry yet?
20
+
21
+ Joe.
@@ -0,0 +1,28 @@
1
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
2
+ c=simple/simple; d=google.com;
3
+ h=Received:From:To:Subject:Date:Message-ID; i=joe@football.example.com;
4
+ s=20230601; t=1615825284; v=1;
5
+ b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
6
+ k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
7
+ s4wwFRRKz/1bksZGSjD8uuSU=
8
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
9
+ c=simple/simple; d=google.com;
10
+ h=Received:From:To:Subject:Date:Message-ID; i=joe@football.example.com;
11
+ s=20230601; t=1615825284; v=1;
12
+ b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
13
+ k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
14
+ s4wwFRRKz/1bksZGSjD8uuSU=
15
+ Received: from client1.football.example.com [192.0.2.1]
16
+ by submitserver.example.com with SUBMISSION;
17
+ Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
18
+ From: Joe SixPack <joe@football.example.com>
19
+ To: Suzie Q <suzie@shopping.example.net>
20
+ Subject: Is dinner ready?
21
+ Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
22
+ Message-ID: <20030712040037.46341.5F8J@football.example.com>
23
+
24
+ Hi.
25
+
26
+ We lost the game. Are you hungry yet?
27
+
28
+ Joe.
@@ -0,0 +1,21 @@
1
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
2
+ c=simple/simple; d=subdomain.google.com;
3
+ h=Received:From:To:Subject:Date:Message-ID; i=joe@google.com;
4
+ s=20230601; t=1615825284; v=1;
5
+ b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
6
+ k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
7
+ s4wwFRRKz/1bksZGSjD8uuSU=
8
+ Received: from client1.football.example.com [192.0.2.1]
9
+ by submitserver.example.com with SUBMISSION;
10
+ Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
11
+ From: Joe SixPack <joe@google.com>
12
+ To: Suzie Q <suzie@shopping.example.net>
13
+ Subject: Is dinner ready?
14
+ Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
15
+ Message-ID: <20030712040037.46341.5F8J@football.example.com>
16
+
17
+ Hi.
18
+
19
+ We lost the game. Are you hungry yet?
20
+
21
+ Joe.
@@ -0,0 +1,21 @@
1
+ DKIM-Signature: a=rsa-sha256; bh=2jUSOH9NhtVGCQWNr9BrIAPreKQjO6Sn7XIkfJVOzv8=;
2
+ c=simple/simple; d=foobar.xyz;
3
+ h=Received:From:To:Subject:Date:Message-ID; i=joe@football.example.com;
4
+ s=wertyu; t=1615825284; v=1;
5
+ b=Xh4Ujb2wv5x54gXtulCiy4C0e+plRm6pZ4owF+kICpYzs/8WkTVIDBrzhJP0DAYCpnL62T0G
6
+ k+0OH8pi/yqETVjKtKk+peMnNvKkut0GeWZMTze0bfq3/JUK3Ln3jTzzpXxrgVnvBxeY9EZIL4g
7
+ s4wwFRRKz/1bksZGSjD8uuSU=
8
+ Received: from client1.football.example.com [192.0.2.1]
9
+ by submitserver.example.com with SUBMISSION;
10
+ Fri, 11 Jul 2003 21:01:54 -0700 (PDT)
11
+ From: Joe SixPack <joe@football.example.com>
12
+ To: Suzie Q <suzie@shopping.example.net>
13
+ Subject: Is dinner ready?
14
+ Date: Fri, 11 Jul 2003 21:00:37 -0700 (PDT)
15
+ Message-ID: <20030712040037.46341.5F8J@football.example.com>
16
+
17
+ Hi.
18
+
19
+ We lost the game. Are you hungry yet?
20
+
21
+ Joe.
@@ -0,0 +1,261 @@
1
+ import {
2
+ describe,
3
+ expect,
4
+ it,
5
+ vi,
6
+ beforeEach,
7
+ type MockInstance,
8
+ afterEach,
9
+ } from "vitest";
10
+
11
+ import { createExtensionWebProofProvider } from "../webProof";
12
+ import { createVlayerClient } from "./client";
13
+ import { type BrandedHash, type VlayerClient } from "types/vlayer";
14
+ import { ZkProvingStatus } from "../../web-proof-commons";
15
+ import createFetchMock from "vitest-fetch-mock";
16
+
17
+ declare const global: {
18
+ chrome: object;
19
+ };
20
+
21
+ const fetchMocker = createFetchMock(vi);
22
+
23
+ beforeEach(() => {
24
+ fetchMocker.enableMocks();
25
+ global.chrome = {
26
+ runtime: {
27
+ sendMessage: vi.fn(),
28
+ connect: vi.fn().mockImplementation(() => {
29
+ return {
30
+ onMessage: {
31
+ addListener: vi.fn(),
32
+ },
33
+ postMessage: vi.fn(),
34
+ };
35
+ }),
36
+ },
37
+ };
38
+ });
39
+
40
+ afterEach(() => {
41
+ fetchMocker.disableMocks();
42
+ });
43
+
44
+ function generateRandomHash() {
45
+ let hash = "0x";
46
+ for (let i = 0; i < 40; ++i) {
47
+ hash += Math.floor(Math.random() * 16).toString(16);
48
+ }
49
+ return hash;
50
+ }
51
+
52
+ describe("Success zk-proving", () => {
53
+ let hashStr: string;
54
+ let zkProvingSpy: MockInstance<(status: ZkProvingStatus) => void>;
55
+ let vlayer: VlayerClient;
56
+
57
+ beforeEach(() => {
58
+ hashStr = generateRandomHash();
59
+ const webProofProvider = createExtensionWebProofProvider();
60
+ zkProvingSpy = vi.spyOn(webProofProvider, "notifyZkProvingStatus");
61
+ vlayer = createVlayerClient({ webProofProvider });
62
+ });
63
+ it("should send message to extension that zkproving started", async () => {
64
+ fetchMocker.mockResponseOnce(() => {
65
+ return {
66
+ body: JSON.stringify({
67
+ result: hashStr,
68
+ }),
69
+ };
70
+ });
71
+
72
+ const result = await vlayer.prove({
73
+ address: `0x${"a".repeat(40)}`,
74
+ functionName: "main",
75
+ proverAbi: [],
76
+ args: [],
77
+ chainId: 42,
78
+ });
79
+
80
+ expect(result.hash).toBe(hashStr);
81
+ expect(zkProvingSpy).toBeCalledTimes(1);
82
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(1, ZkProvingStatus.Proving);
83
+ });
84
+ it("should send message to extension that zkproving is done", async () => {
85
+ fetchMocker.mockResponseOnce(() => {
86
+ return {
87
+ body: JSON.stringify({
88
+ result: hashStr,
89
+ }),
90
+ };
91
+ });
92
+
93
+ await vlayer.prove({
94
+ address: `0x${"a".repeat(40)}`,
95
+ functionName: "main",
96
+ proverAbi: [],
97
+ args: [],
98
+ chainId: 42,
99
+ });
100
+
101
+ fetchMocker.mockResponseOnce(() => {
102
+ return {
103
+ body: JSON.stringify({
104
+ result: {
105
+ state: "done",
106
+ status: 1,
107
+ metrics: {},
108
+ data: {},
109
+ },
110
+ jsonrpc: "2.0",
111
+ id: 1,
112
+ }),
113
+ };
114
+ });
115
+
116
+ const hash = { hash: hashStr } as BrandedHash<[], string>;
117
+ await vlayer.waitForProvingResult({ hash });
118
+
119
+ expect(zkProvingSpy).toBeCalledTimes(2);
120
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(1, ZkProvingStatus.Proving);
121
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(2, ZkProvingStatus.Done);
122
+ });
123
+ it("should notify that zk-proving failed", async () => {
124
+ fetchMocker.mockResponseOnce(() => {
125
+ throw new Error("test");
126
+ });
127
+
128
+ const hash = { hash: hashStr } as BrandedHash<[], string>;
129
+ try {
130
+ await vlayer.waitForProvingResult({ hash });
131
+ } catch (e) {
132
+ console.log("Error waiting for proving result", e);
133
+ }
134
+
135
+ expect(zkProvingSpy).toBeCalledTimes(1);
136
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(1, ZkProvingStatus.Error);
137
+ });
138
+ it("should pass token if present", async () => {
139
+ const userToken = "deadbeef";
140
+
141
+ fetchMocker.mockResponseOnce((req) => {
142
+ const token = (req.headers.get("authorization") || "")
143
+ .split("Bearer ")
144
+ .at(1);
145
+ if (token !== undefined && token === userToken) {
146
+ return {
147
+ body: JSON.stringify({
148
+ result: hashStr,
149
+ }),
150
+ };
151
+ }
152
+ return {
153
+ body: JSON.stringify({
154
+ jsonrpc: "2.0",
155
+ id: 1,
156
+ error: {
157
+ code: -32603,
158
+ message:
159
+ "Gas meter error: RPC error: HTTP error: HTTP status client error (401 Unauthorized) for url (http://localhost:3000)",
160
+ },
161
+ }),
162
+ };
163
+ });
164
+
165
+ const result = await vlayer.prove({
166
+ address: `0x${"a".repeat(40)}`,
167
+ functionName: "main",
168
+ proverAbi: [],
169
+ args: [],
170
+ chainId: 42,
171
+ token: userToken,
172
+ });
173
+ expect(result.hash).toBe(hashStr);
174
+ expect(zkProvingSpy).toBeCalledTimes(1);
175
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(1, ZkProvingStatus.Proving);
176
+ });
177
+ });
178
+
179
+ describe("Failed zk-proving", () => {
180
+ let zkProvingSpy: MockInstance<(status: ZkProvingStatus) => void>;
181
+ let vlayer: VlayerClient;
182
+ let hashStr: string;
183
+
184
+ beforeEach(() => {
185
+ hashStr = generateRandomHash();
186
+ const webProofProvider = createExtensionWebProofProvider();
187
+ zkProvingSpy = vi.spyOn(webProofProvider, "notifyZkProvingStatus");
188
+ vlayer = createVlayerClient({ webProofProvider });
189
+ });
190
+ it("should send message to extension that zkproving started and then that failed when server error 500", async () => {
191
+ fetchMocker.mockResponseOnce((req) => {
192
+ if (req.url === "http://127.0.0.1:3000/") {
193
+ return {
194
+ status: 500,
195
+ };
196
+ }
197
+ return {};
198
+ });
199
+
200
+ try {
201
+ const hash = await vlayer.prove({
202
+ address: `0x${"a".repeat(40)}`,
203
+ functionName: "main",
204
+ proverAbi: [],
205
+ args: [],
206
+ chainId: 42,
207
+ });
208
+ await vlayer.waitForProvingResult({ hash });
209
+ } catch (e) {
210
+ console.log("Error waiting for proving result", e);
211
+ }
212
+
213
+ expect(zkProvingSpy).toBeCalledTimes(2);
214
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(1, ZkProvingStatus.Proving);
215
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(2, ZkProvingStatus.Error);
216
+ });
217
+ it("should send message to extension that zkproving started and then that failed when computation failed at any stage", async () => {
218
+ fetchMocker.mockResponseOnce(() => {
219
+ return {
220
+ body: JSON.stringify({
221
+ result: hashStr,
222
+ }),
223
+ };
224
+ });
225
+
226
+ const hash = await vlayer.prove({
227
+ address: `0x${"a".repeat(40)}`,
228
+ functionName: "main",
229
+ proverAbi: [],
230
+ args: [],
231
+ chainId: 42,
232
+ });
233
+
234
+ fetchMocker.mockResponseOnce(() => {
235
+ return {
236
+ body: JSON.stringify({
237
+ result: {
238
+ state: "preflight",
239
+ status: 0,
240
+ metrics: {},
241
+ error: "Preflight error: ...",
242
+ },
243
+ jsonrpc: "2.0",
244
+ id: 1,
245
+ }),
246
+ };
247
+ });
248
+
249
+ try {
250
+ await vlayer.waitForProvingResult({ hash });
251
+ } catch (e) {
252
+ expect((e as Error).message).toMatch(
253
+ "Preflight failed with error: Preflight error: ...",
254
+ );
255
+ }
256
+
257
+ expect(zkProvingSpy).toBeCalledTimes(2);
258
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(1, ZkProvingStatus.Proving);
259
+ expect(zkProvingSpy).toHaveBeenNthCalledWith(2, ZkProvingStatus.Error);
260
+ });
261
+ });