@prosopo/provider 1.0.1 → 2.0.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 (191) hide show
  1. package/README.md +0 -258
  2. package/dist/api/admin.d.ts +2 -2
  3. package/dist/api/admin.d.ts.map +1 -1
  4. package/dist/api/admin.js +5 -64
  5. package/dist/api/admin.js.map +1 -1
  6. package/dist/api/authMiddleware.d.ts +4 -4
  7. package/dist/api/authMiddleware.d.ts.map +1 -1
  8. package/dist/api/authMiddleware.js +15 -29
  9. package/dist/api/authMiddleware.js.map +1 -1
  10. package/dist/api/captcha.d.ts +2 -2
  11. package/dist/api/captcha.d.ts.map +1 -1
  12. package/dist/api/captcha.js +64 -55
  13. package/dist/api/captcha.js.map +1 -1
  14. package/dist/api/captchaScheduler.d.ts +4 -0
  15. package/dist/api/captchaScheduler.d.ts.map +1 -0
  16. package/dist/api/captchaScheduler.js +22 -0
  17. package/dist/api/captchaScheduler.js.map +1 -0
  18. package/dist/api/errorHandler.d.ts +3 -3
  19. package/dist/api/errorHandler.d.ts.map +1 -1
  20. package/dist/api/errorHandler.js +7 -3
  21. package/dist/api/errorHandler.js.map +1 -1
  22. package/dist/api/verify.d.ts +2 -2
  23. package/dist/api/verify.d.ts.map +1 -1
  24. package/dist/api/verify.js +37 -29
  25. package/dist/api/verify.js.map +1 -1
  26. package/dist/cjs/api/admin.cjs +2 -89
  27. package/dist/cjs/api/authMiddleware.cjs +1 -15
  28. package/dist/cjs/api/captcha.cjs +89 -52
  29. package/dist/cjs/api/captchaScheduler.cjs +20 -0
  30. package/dist/cjs/api/errorHandler.cjs +3 -1
  31. package/dist/cjs/api/verify.cjs +54 -28
  32. package/dist/cjs/index.cjs +2 -3
  33. package/dist/cjs/tasks/dataset/datasetTasks.cjs +68 -0
  34. package/dist/cjs/tasks/dataset/datasetTasksUtils.cjs +34 -0
  35. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +277 -0
  36. package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasksUtils.cjs +25 -0
  37. package/dist/cjs/tasks/powCaptcha/powTasks.cjs +107 -0
  38. package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +55 -0
  39. package/dist/cjs/tasks/tasks.cjs +21 -512
  40. package/dist/cjs/util.cjs +32 -19
  41. package/dist/index.d.ts +7 -7
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +7 -7
  44. package/dist/index.js.map +1 -1
  45. package/dist/tasks/dataset/datasetTasks.d.ts +15 -0
  46. package/dist/tasks/dataset/datasetTasks.d.ts.map +1 -0
  47. package/dist/tasks/dataset/datasetTasks.js +40 -0
  48. package/dist/tasks/dataset/datasetTasks.js.map +1 -0
  49. package/dist/tasks/dataset/datasetTasksUtils.d.ts +3 -0
  50. package/dist/tasks/dataset/datasetTasksUtils.d.ts.map +1 -0
  51. package/dist/tasks/dataset/datasetTasksUtils.js +34 -0
  52. package/dist/tasks/dataset/datasetTasksUtils.js.map +1 -0
  53. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts +28 -0
  54. package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -0
  55. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +212 -0
  56. package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -0
  57. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts +7 -0
  58. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +1 -0
  59. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js +18 -0
  60. package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +1 -0
  61. package/dist/tasks/index.d.ts +1 -1
  62. package/dist/tasks/index.d.ts.map +1 -1
  63. package/dist/tasks/index.js +1 -1
  64. package/dist/tasks/index.js.map +1 -1
  65. package/dist/tasks/powCaptcha/powTasks.d.ts +13 -0
  66. package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -0
  67. package/dist/tasks/powCaptcha/powTasks.js +66 -0
  68. package/dist/tasks/powCaptcha/powTasks.js.map +1 -0
  69. package/dist/tasks/powCaptcha/powTasksUtils.d.ts +5 -0
  70. package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +1 -0
  71. package/dist/tasks/powCaptcha/powTasksUtils.js +49 -0
  72. package/dist/tasks/powCaptcha/powTasksUtils.js.map +1 -0
  73. package/dist/tasks/tasks.d.ts +12 -42
  74. package/dist/tasks/tasks.d.ts.map +1 -1
  75. package/dist/tasks/tasks.js +13 -419
  76. package/dist/tasks/tasks.js.map +1 -1
  77. package/dist/tests/index.d.ts +2 -0
  78. package/dist/tests/index.d.ts.map +1 -0
  79. package/dist/tests/index.js +2 -0
  80. package/dist/tests/index.js.map +1 -0
  81. package/dist/tests/integration/imgCaptcha.test.d.ts +2 -0
  82. package/dist/tests/integration/imgCaptcha.test.d.ts.map +1 -0
  83. package/dist/tests/integration/imgCaptcha.test.js +111 -0
  84. package/dist/tests/integration/imgCaptcha.test.js.map +1 -0
  85. package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts +32 -0
  86. package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts.map +1 -0
  87. package/dist/tests/integration/mocks/solvedTestCaptchas.js +1042 -0
  88. package/dist/tests/integration/mocks/solvedTestCaptchas.js.map +1 -0
  89. package/dist/tests/integration/powCaptcha.test.d.ts +2 -0
  90. package/dist/tests/integration/powCaptcha.test.d.ts.map +1 -0
  91. package/dist/tests/integration/powCaptcha.test.js +171 -0
  92. package/dist/tests/integration/powCaptcha.test.js.map +1 -0
  93. package/dist/tests/unit/api/authMiddleware.test.d.ts +2 -0
  94. package/dist/tests/unit/api/authMiddleware.test.d.ts.map +1 -0
  95. package/dist/tests/unit/api/authMiddleware.test.js +87 -0
  96. package/dist/tests/unit/api/authMiddleware.test.js.map +1 -0
  97. package/dist/tests/unit/api/captchaScheduler.test.d.ts +2 -0
  98. package/dist/tests/unit/api/captchaScheduler.test.d.ts.map +1 -0
  99. package/dist/tests/unit/api/captchaScheduler.test.js +47 -0
  100. package/dist/tests/unit/api/captchaScheduler.test.js.map +1 -0
  101. package/dist/tests/unit/api/errorHandler.test.d.ts +2 -0
  102. package/dist/tests/unit/api/errorHandler.test.d.ts.map +1 -0
  103. package/dist/tests/unit/api/errorHandler.test.js +65 -0
  104. package/dist/tests/unit/api/errorHandler.test.js.map +1 -0
  105. package/dist/tests/unit/tasks/dataset/datasetTasks.test.d.ts +2 -0
  106. package/dist/tests/unit/tasks/dataset/datasetTasks.test.d.ts.map +1 -0
  107. package/dist/tests/unit/tasks/dataset/datasetTasks.test.js +88 -0
  108. package/dist/tests/unit/tasks/dataset/datasetTasks.test.js.map +1 -0
  109. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.test.d.ts +2 -0
  110. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.test.d.ts.map +1 -0
  111. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.test.js +75 -0
  112. package/dist/tests/unit/tasks/dataset/datasetTasksUtils.test.js.map +1 -0
  113. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.test.d.ts +2 -0
  114. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.test.d.ts.map +1 -0
  115. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.test.js +260 -0
  116. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.test.js.map +1 -0
  117. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.test.d.ts +2 -0
  118. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.test.d.ts.map +1 -0
  119. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.test.js +46 -0
  120. package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.test.js.map +1 -0
  121. package/dist/tests/unit/tasks/powCaptcha/powTasks.test.d.ts +2 -0
  122. package/dist/tests/unit/tasks/powCaptcha/powTasks.test.d.ts.map +1 -0
  123. package/dist/tests/unit/tasks/powCaptcha/powTasks.test.js +133 -0
  124. package/dist/tests/unit/tasks/powCaptcha/powTasks.test.js.map +1 -0
  125. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.test.d.ts +2 -0
  126. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.test.d.ts.map +1 -0
  127. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.test.js +94 -0
  128. package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.test.js.map +1 -0
  129. package/dist/util.d.ts +2 -2
  130. package/dist/util.d.ts.map +1 -1
  131. package/dist/util.js +10 -8
  132. package/dist/util.js.map +1 -1
  133. package/package.json +74 -86
  134. package/vite.cjs.config.ts +3 -3
  135. package/vite.test.config.ts +12 -12
  136. package/dist/batch/commitments.d.ts +0 -24
  137. package/dist/batch/commitments.d.ts.map +0 -1
  138. package/dist/batch/commitments.js +0 -130
  139. package/dist/batch/commitments.js.map +0 -1
  140. package/dist/batch/index.d.ts +0 -2
  141. package/dist/batch/index.d.ts.map +0 -1
  142. package/dist/batch/index.js +0 -2
  143. package/dist/batch/index.js.map +0 -1
  144. package/dist/cjs/batch/commitments.cjs +0 -158
  145. package/dist/cjs/batch/index.cjs +0 -4
  146. package/dist/scheduler.d.ts +0 -4
  147. package/dist/scheduler.d.ts.map +0 -1
  148. package/dist/scheduler.js +0 -21
  149. package/dist/scheduler.js.map +0 -1
  150. package/dist/tests/accounts.d.ts +0 -12
  151. package/dist/tests/accounts.d.ts.map +0 -1
  152. package/dist/tests/accounts.js +0 -35
  153. package/dist/tests/accounts.js.map +0 -1
  154. package/dist/tests/contract/helpers.test.d.ts +0 -6
  155. package/dist/tests/contract/helpers.test.d.ts.map +0 -1
  156. package/dist/tests/contract/helpers.test.js +0 -54
  157. package/dist/tests/contract/helpers.test.js.map +0 -1
  158. package/dist/tests/dataUtils/DatabaseAccounts.d.ts +0 -35
  159. package/dist/tests/dataUtils/DatabaseAccounts.d.ts.map +0 -1
  160. package/dist/tests/dataUtils/DatabaseAccounts.js +0 -84
  161. package/dist/tests/dataUtils/DatabaseAccounts.js.map +0 -1
  162. package/dist/tests/dataUtils/DatabasePopulator.d.ts +0 -73
  163. package/dist/tests/dataUtils/DatabasePopulator.d.ts.map +0 -1
  164. package/dist/tests/dataUtils/DatabasePopulator.js +0 -326
  165. package/dist/tests/dataUtils/DatabasePopulator.js.map +0 -1
  166. package/dist/tests/dataUtils/dapp-example-contract/dapp.json +0 -648
  167. package/dist/tests/dataUtils/dapp-example-contract/loadFiles.d.ts +0 -4
  168. package/dist/tests/dataUtils/dapp-example-contract/loadFiles.d.ts.map +0 -1
  169. package/dist/tests/dataUtils/dapp-example-contract/loadFiles.js +0 -27
  170. package/dist/tests/dataUtils/dapp-example-contract/loadFiles.js.map +0 -1
  171. package/dist/tests/dataUtils/funds.d.ts +0 -9
  172. package/dist/tests/dataUtils/funds.d.ts.map +0 -1
  173. package/dist/tests/dataUtils/funds.js +0 -105
  174. package/dist/tests/dataUtils/funds.js.map +0 -1
  175. package/dist/tests/dataUtils/populateDatabase.d.ts +0 -16
  176. package/dist/tests/dataUtils/populateDatabase.d.ts.map +0 -1
  177. package/dist/tests/dataUtils/populateDatabase.js +0 -72
  178. package/dist/tests/dataUtils/populateDatabase.js.map +0 -1
  179. package/dist/tests/getUser.d.ts +0 -4
  180. package/dist/tests/getUser.d.ts.map +0 -1
  181. package/dist/tests/getUser.js +0 -18
  182. package/dist/tests/getUser.js.map +0 -1
  183. package/dist/tests/tasks/tasks.test.d.ts +0 -6
  184. package/dist/tests/tasks/tasks.test.d.ts.map +0 -1
  185. package/dist/tests/tasks/tasks.test.js +0 -635
  186. package/dist/tests/tasks/tasks.test.js.map +0 -1
  187. package/dist/tests/util.test.d.ts +0 -2
  188. package/dist/tests/util.test.d.ts.map +0 -1
  189. package/dist/tests/util.test.js +0 -23
  190. package/dist/tests/util.test.js.map +0 -1
  191. package/typedoc.config.js +0 -19
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const util$1 = require("@polkadot/util");
4
+ const utilCrypto = require("@polkadot/util-crypto");
5
+ const common = require("@prosopo/common");
6
+ const datasets = require("@prosopo/datasets");
7
+ const types = require("@prosopo/types");
8
+ const util$2 = require("@prosopo/util");
9
+ const util = require("../../util.cjs");
10
+ const imgCaptchaTasksUtils = require("./imgCaptchaTasksUtils.cjs");
11
+ class ImgCaptchaManager {
12
+ constructor(db, pair, logger, captchaConfig) {
13
+ this.db = db;
14
+ this.pair = pair;
15
+ this.logger = logger;
16
+ this.captchaConfig = captchaConfig;
17
+ }
18
+ async getCaptchaWithProof(datasetId, solved, size) {
19
+ const captchaDocs = await this.db.getRandomCaptcha(solved, datasetId, size);
20
+ if (!captchaDocs) {
21
+ throw new common.ProsopoEnvError("DATABASE.CAPTCHA_GET_FAILED", {
22
+ context: {
23
+ failedFuncName: this.getCaptchaWithProof.name,
24
+ datasetId,
25
+ solved,
26
+ size
27
+ }
28
+ });
29
+ }
30
+ return captchaDocs;
31
+ }
32
+ async getRandomCaptchasAndRequestHash(datasetId, userAccount) {
33
+ const dataset = await this.db.getDatasetDetails(datasetId);
34
+ if (!dataset) {
35
+ throw new common.ProsopoEnvError("DATABASE.DATASET_GET_FAILED", {
36
+ context: {
37
+ failedFuncName: this.getRandomCaptchasAndRequestHash.name,
38
+ dataset,
39
+ datasetId
40
+ }
41
+ });
42
+ }
43
+ const unsolvedCount = Math.abs(
44
+ Math.trunc(this.captchaConfig.unsolved.count)
45
+ );
46
+ const solvedCount = Math.abs(
47
+ Math.trunc(this.captchaConfig.solved.count)
48
+ );
49
+ if (!solvedCount) {
50
+ throw new common.ProsopoEnvError("CONFIG.INVALID_CAPTCHA_NUMBER");
51
+ }
52
+ const solved = await this.getCaptchaWithProof(datasetId, true, solvedCount);
53
+ let unsolved = [];
54
+ if (unsolvedCount) {
55
+ unsolved = await this.getCaptchaWithProof(
56
+ datasetId,
57
+ false,
58
+ unsolvedCount
59
+ );
60
+ }
61
+ const captchas = util.shuffleArray([...solved, ...unsolved]);
62
+ const salt = utilCrypto.randomAsHex();
63
+ const requestHash = datasets.computePendingRequestHash(
64
+ captchas.map((c) => c.captchaId),
65
+ userAccount,
66
+ salt
67
+ );
68
+ const currentTime = Date.now();
69
+ const signedTimestamp = util$1.u8aToHex(
70
+ this.pair.sign(util$1.stringToHex(currentTime.toString()))
71
+ );
72
+ const timeLimit = captchas.map((captcha) => captcha.timeLimitMs || types.DEFAULT_IMAGE_CAPTCHA_TIMEOUT).reduce((a, b) => a + b, 0);
73
+ const deadlineTs = timeLimit + currentTime;
74
+ const currentBlockNumber = 0;
75
+ await this.db.storeDappUserPending(
76
+ userAccount,
77
+ requestHash,
78
+ salt,
79
+ deadlineTs,
80
+ currentBlockNumber
81
+ );
82
+ return {
83
+ captchas,
84
+ requestHash,
85
+ timestamp: currentTime,
86
+ signedTimestamp
87
+ };
88
+ }
89
+ /**
90
+ * Validate and store the text captcha solution(s) from the Dapp User in a web2 environment
91
+ * @param {string} userAccount
92
+ * @param {string} dappAccount
93
+ * @param {string} requestHash
94
+ * @param {JSON} captchas
95
+ * @param {string} requestHashSignature
96
+ * @param timestamp
97
+ * @param timestampSignature
98
+ * @return {Promise<DappUserSolutionResult>} result containing the contract event
99
+ */
100
+ async dappUserSolution(userAccount, dappAccount, requestHash, captchas, requestHashSignature, timestamp, timestampSignature) {
101
+ const verification = utilCrypto.signatureVerify(
102
+ util$1.stringToHex(requestHash),
103
+ requestHashSignature,
104
+ userAccount
105
+ );
106
+ if (!verification.isValid) {
107
+ this.logger.info("Invalid requestHash signature");
108
+ throw new common.ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
109
+ context: { failedFuncName: this.dappUserSolution.name, userAccount }
110
+ });
111
+ }
112
+ const timestampSigVerify = utilCrypto.signatureVerify(
113
+ util$1.stringToHex(timestamp.toString()),
114
+ timestampSignature,
115
+ this.pair.address
116
+ );
117
+ if (!timestampSigVerify.isValid) {
118
+ this.logger.info("Invalid timestamp signature");
119
+ throw new common.ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
120
+ context: {
121
+ failedFuncName: this.dappUserSolution.name,
122
+ userAccount,
123
+ error: "timestamp signature is invalid"
124
+ }
125
+ });
126
+ }
127
+ let response = {
128
+ captchas: [],
129
+ verified: false
130
+ };
131
+ const pendingRecord = await this.db.getDappUserPending(requestHash);
132
+ const unverifiedCaptchaIds = captchas.map((captcha) => captcha.captchaId);
133
+ const pendingRequest = await this.validateDappUserSolutionRequestIsPending(
134
+ requestHash,
135
+ pendingRecord,
136
+ userAccount,
137
+ unverifiedCaptchaIds
138
+ );
139
+ console.log("Pending request", pendingRequest);
140
+ if (pendingRequest) {
141
+ const { storedCaptchas, receivedCaptchas, captchaIds } = await this.validateReceivedCaptchasAgainstStoredCaptchas(captchas);
142
+ const { tree, commitmentId } = imgCaptchaTasksUtils.buildTreeAndGetCommitmentId(receivedCaptchas);
143
+ const datasetId = util$2.at(storedCaptchas, 0).datasetId;
144
+ if (!datasetId) {
145
+ throw new common.ProsopoEnvError("CAPTCHA.ID_MISMATCH", {
146
+ context: { failedFuncName: this.dappUserSolution.name }
147
+ });
148
+ }
149
+ const userSignature = util$1.hexToU8a(requestHashSignature);
150
+ await this.db.updateDappUserPendingStatus(requestHash);
151
+ const commit = {
152
+ id: commitmentId,
153
+ userAccount,
154
+ dappContract: dappAccount,
155
+ providerAccount: this.pair.address,
156
+ datasetId,
157
+ status: types.CaptchaStatus.pending,
158
+ userSignature: Array.from(userSignature),
159
+ requestedAt: pendingRecord.requestedAtBlock,
160
+ // TODO is this correct or should it be block number?
161
+ completedAt: 0,
162
+ //temp
163
+ processed: false,
164
+ batched: false,
165
+ stored: false,
166
+ requestedAtTimestamp: timestamp
167
+ };
168
+ await this.db.storeDappUserSolution(receivedCaptchas, commit);
169
+ console.log(receivedCaptchas);
170
+ console.log(storedCaptchas);
171
+ if (datasets.compareCaptchaSolutions(receivedCaptchas, storedCaptchas)) {
172
+ response = {
173
+ captchas: captchaIds.map((id) => ({
174
+ captchaId: id,
175
+ proof: tree.proof(id)
176
+ })),
177
+ verified: true
178
+ };
179
+ await this.db.approveDappUserCommitment(commitmentId);
180
+ } else {
181
+ response = {
182
+ captchas: captchaIds.map((id) => ({
183
+ captchaId: id,
184
+ proof: [[]]
185
+ })),
186
+ verified: false
187
+ };
188
+ }
189
+ } else {
190
+ this.logger.info("Request hash not found");
191
+ }
192
+ return response;
193
+ }
194
+ /**
195
+ * Validate length of received captchas array matches length of captchas found in database
196
+ * Validate that the datasetId is the same for all captchas and is equal to the datasetId on the stored captchas
197
+ */
198
+ async validateReceivedCaptchasAgainstStoredCaptchas(captchas) {
199
+ const receivedCaptchas = datasets.parseAndSortCaptchaSolutions(captchas);
200
+ const captchaIds = receivedCaptchas.map((captcha) => captcha.captchaId);
201
+ const storedCaptchas = await this.db.getCaptchaById(captchaIds);
202
+ if (!storedCaptchas || receivedCaptchas.length !== storedCaptchas.length) {
203
+ throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_ID", {
204
+ context: {
205
+ failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
206
+ captchas
207
+ }
208
+ });
209
+ }
210
+ if (!storedCaptchas.every(
211
+ (captcha) => captcha.datasetId === util$2.at(storedCaptchas, 0).datasetId
212
+ )) {
213
+ throw new common.ProsopoEnvError("CAPTCHA.DIFFERENT_DATASET_IDS", {
214
+ context: {
215
+ failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
216
+ captchas
217
+ }
218
+ });
219
+ }
220
+ return { storedCaptchas, receivedCaptchas, captchaIds };
221
+ }
222
+ /**
223
+ * Validate that a Dapp User is responding to their own pending captcha request
224
+ * @param {string} requestHash
225
+ * @param {PendingCaptchaRequest} pendingRecord
226
+ * @param {string} userAccount
227
+ * @param {string[]} captchaIds
228
+ */
229
+ async validateDappUserSolutionRequestIsPending(requestHash, pendingRecord, userAccount, captchaIds) {
230
+ const currentTime = Date.now();
231
+ if (!pendingRecord) {
232
+ this.logger.info("No pending record found");
233
+ return false;
234
+ }
235
+ if (pendingRecord.deadlineTimestamp < currentTime) {
236
+ this.logger.info("Deadline for responding to captcha has expired");
237
+ return false;
238
+ }
239
+ if (pendingRecord) {
240
+ const pendingHashComputed = datasets.computePendingRequestHash(
241
+ captchaIds,
242
+ userAccount,
243
+ pendingRecord.salt
244
+ );
245
+ return requestHash === pendingHashComputed;
246
+ }
247
+ return false;
248
+ }
249
+ /*
250
+ * Get dapp user solution from database
251
+ */
252
+ async getDappUserCommitmentById(commitmentId) {
253
+ const dappUserSolution = await this.db.getDappUserCommitmentById(commitmentId);
254
+ if (!dappUserSolution) {
255
+ throw new common.ProsopoEnvError("CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND", {
256
+ context: {
257
+ failedFuncName: this.getDappUserCommitmentById.name,
258
+ commitmentId
259
+ }
260
+ });
261
+ }
262
+ return dappUserSolution;
263
+ }
264
+ /* Check if dapp user has verified solution in cache */
265
+ async getDappUserCommitmentByAccount(userAccount) {
266
+ const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(userAccount);
267
+ if (dappUserSolutions.length > 0) {
268
+ for (const dappUserSolution of dappUserSolutions) {
269
+ if (dappUserSolution.status === types.CaptchaStatus.approved) {
270
+ return dappUserSolution;
271
+ }
272
+ }
273
+ }
274
+ return void 0;
275
+ }
276
+ }
277
+ exports.ImgCaptchaManager = ImgCaptchaManager;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const common = require("@prosopo/common");
4
+ const datasets = require("@prosopo/datasets");
5
+ const buildTreeAndGetCommitmentId = (captchaSolutions) => {
6
+ const tree = new datasets.CaptchaMerkleTree();
7
+ const solutionsHashed = captchaSolutions.map(
8
+ (captcha) => datasets.computeCaptchaSolutionHash(captcha)
9
+ );
10
+ tree.build(solutionsHashed);
11
+ const commitmentId = tree.root?.hash;
12
+ if (!commitmentId) {
13
+ throw new common.ProsopoEnvError(
14
+ "CONTRACT.CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST",
15
+ {
16
+ context: {
17
+ failedFuncName: buildTreeAndGetCommitmentId.name,
18
+ commitmentId
19
+ }
20
+ }
21
+ );
22
+ }
23
+ return { tree, commitmentId };
24
+ };
25
+ exports.buildTreeAndGetCommitmentId = buildTreeAndGetCommitmentId;
@@ -0,0 +1,107 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const util = require("@polkadot/util");
4
+ const common = require("@prosopo/common");
5
+ const types = require("@prosopo/types");
6
+ const util$1 = require("@prosopo/util");
7
+ const powTasksUtils = require("./powTasksUtils.cjs");
8
+ class PowCaptchaManager {
9
+ constructor(pair, db) {
10
+ this.pair = pair;
11
+ this.db = db;
12
+ this.POW_SEPARATOR = types.POW_SEPARATOR;
13
+ }
14
+ /**
15
+ * @description Generates a PoW Captcha for a given user and dapp
16
+ *
17
+ * @param {string} userAccount - user that is solving the captcha
18
+ * @param {string} dappAccount - dapp that is requesting the captcha
19
+ * @param origin - not currently used
20
+ */
21
+ async getPowCaptchaChallenge(userAccount, dappAccount, origin) {
22
+ const difficulty = 4;
23
+ const timestamp = Date.now();
24
+ const challenge = `${timestamp}___${userAccount}___${dappAccount}`;
25
+ const challengeSignature = util.u8aToHex(this.pair.sign(util.stringToHex(challenge)));
26
+ const timestampSignature = util.u8aToHex(
27
+ this.pair.sign(util.stringToHex(timestamp.toString()))
28
+ );
29
+ return {
30
+ challenge,
31
+ difficulty,
32
+ signature: challengeSignature,
33
+ timestamp,
34
+ timestampSignature
35
+ };
36
+ }
37
+ /**
38
+ * @description Verifies a PoW Captcha for a given user and dapp
39
+ *
40
+ * @param {string} challenge - the starting string for the PoW challenge
41
+ * @param {string} difficulty - how many leading zeroes the solution must have
42
+ * @param {string} signature - proof that the Provider provided the challenge
43
+ * @param {string} nonce - the string that the user has found that satisfies the PoW challenge
44
+ * @param {number} timeout - the time in milliseconds since the Provider was selected to provide the PoW captcha
45
+ * @param timestampSignature
46
+ */
47
+ async verifyPowCaptchaSolution(challenge, difficulty, signature, nonce, timeout, timestampSignature) {
48
+ powTasksUtils.checkRecentPowSolution(challenge, timeout);
49
+ const challengeSplit = challenge.split(this.POW_SEPARATOR);
50
+ const timestamp = parseInt(util$1.at(challengeSplit, 0));
51
+ const userAccount = util$1.at(challengeSplit, 1);
52
+ const dappAccount = util$1.at(challengeSplit, 2);
53
+ powTasksUtils.checkPowSignature(
54
+ timestamp.toString(),
55
+ timestampSignature,
56
+ userAccount,
57
+ types.ApiParams.timestamp
58
+ );
59
+ powTasksUtils.checkPowSignature(
60
+ challenge,
61
+ signature,
62
+ this.pair.address,
63
+ types.ApiParams.challenge
64
+ );
65
+ powTasksUtils.checkPowSolution(nonce, challenge, difficulty);
66
+ await this.db.storePowCaptchaRecord(
67
+ challenge,
68
+ { timestamp, userAccount, dappAccount },
69
+ false
70
+ );
71
+ return true;
72
+ }
73
+ /**
74
+ * @description Verifies a PoW Captcha for a given user and dapp. This is called by the server to verify the user's solution
75
+ * and update the record in the database to show that the user has solved the captcha
76
+ *
77
+ * @param {string} dappAccount - the dapp that is requesting the captcha
78
+ * @param {string} challenge - the starting string for the PoW challenge
79
+ * @param {number} timeout - the time in milliseconds since the Provider was selected to provide the PoW captcha
80
+ */
81
+ async serverVerifyPowCaptchaSolution(dappAccount, challenge, timeout) {
82
+ const challengeRecord = await this.db.getPowCaptchaRecordByChallenge(challenge);
83
+ if (!challengeRecord) {
84
+ throw new common.ProsopoEnvError("DATABASE.CAPTCHA_GET_FAILED", {
85
+ context: {
86
+ failedFuncName: this.serverVerifyPowCaptchaSolution.name,
87
+ challenge
88
+ }
89
+ });
90
+ }
91
+ if (challengeRecord.checked) return false;
92
+ const challengeDappAccount = challengeRecord.dappAccount;
93
+ if (dappAccount !== challengeDappAccount) {
94
+ throw new common.ProsopoEnvError("CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND", {
95
+ context: {
96
+ failedFuncName: this.serverVerifyPowCaptchaSolution.name,
97
+ dappAccount,
98
+ challengeDappAccount
99
+ }
100
+ });
101
+ }
102
+ powTasksUtils.checkRecentPowSolution(challenge, timeout);
103
+ await this.db.updatePowCaptchaRecord(challengeRecord.challenge, true);
104
+ return true;
105
+ }
106
+ }
107
+ exports.PowCaptchaManager = PowCaptchaManager;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const sha256 = require("@noble/hashes/sha256");
4
+ const util = require("@polkadot/util");
5
+ const utilCrypto = require("@polkadot/util-crypto");
6
+ const common = require("@prosopo/common");
7
+ const contract = require("@prosopo/contract");
8
+ const validateSolution = (nonce, challenge, difficulty) => Array.from(sha256.sha256(new TextEncoder().encode(nonce + challenge))).map((byte) => byte.toString(16).padStart(2, "0")).join("").startsWith("0".repeat(difficulty));
9
+ const checkPowSolution = (nonce, challenge, difficulty) => {
10
+ const solutionValid = validateSolution(nonce, challenge, difficulty);
11
+ if (!solutionValid) {
12
+ throw new common.ProsopoContractError("API.CAPTCHA_FAILED", {
13
+ context: {
14
+ ERROR: "Captcha solution is invalid",
15
+ failedFuncName: checkPowSolution.name,
16
+ nonce,
17
+ challenge,
18
+ difficulty
19
+ }
20
+ });
21
+ }
22
+ };
23
+ const checkPowSignature = (challenge, signature, address, signatureType) => {
24
+ const signatureVerification = utilCrypto.signatureVerify(
25
+ util.stringToHex(challenge),
26
+ signature,
27
+ address
28
+ );
29
+ if (!signatureVerification.isValid) {
30
+ throw new common.ProsopoContractError("GENERAL.INVALID_SIGNATURE", {
31
+ context: {
32
+ ERROR: `Signature is invalid for this message: ${signatureType}`,
33
+ failedFuncName: checkPowSignature.name,
34
+ signature,
35
+ signatureType
36
+ }
37
+ });
38
+ }
39
+ };
40
+ const checkRecentPowSolution = (challenge, timeout) => {
41
+ const recent = contract.verifyRecency(challenge, timeout);
42
+ if (!recent) {
43
+ throw new common.ProsopoContractError("CONTRACT.INVALID_BLOCKHASH", {
44
+ context: {
45
+ ERROR: `Block in which the Provider was selected must be within the last ${timeout / 1e3} seconds`,
46
+ failedFuncName: checkRecentPowSolution.name,
47
+ challenge
48
+ }
49
+ });
50
+ }
51
+ };
52
+ exports.checkPowSignature = checkPowSignature;
53
+ exports.checkPowSolution = checkPowSolution;
54
+ exports.checkRecentPowSolution = checkRecentPowSolution;
55
+ exports.validateSolution = validateSolution;