@prosopo/procaptcha 0.2.0 → 0.2.2
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.
- package/dist/api/HttpClientBase.js +1 -1
- package/dist/api/HttpClientBase.js.map +1 -1
- package/dist/cjs/api/Extension.cjs +4 -0
- package/dist/cjs/api/ExtensionWeb2.cjs +88 -0
- package/dist/cjs/api/ExtensionWeb3.cjs +25 -0
- package/dist/cjs/api/HttpClientBase.cjs +17 -0
- package/dist/cjs/api/errors.cjs +32 -0
- package/dist/cjs/api/handlers.cjs +11 -0
- package/dist/cjs/api/index.cjs +6 -0
- package/dist/cjs/datasets/dist/captcha/captcha.cjs +142 -0
- package/dist/cjs/datasets/dist/captcha/dataset.cjs +87 -0
- package/dist/cjs/datasets/dist/captcha/index.cjs +27 -0
- package/dist/cjs/datasets/dist/captcha/merkle.cjs +106 -0
- package/dist/cjs/datasets/dist/captcha/util.cjs +12 -0
- package/dist/cjs/datasets/dist/index.cjs +2 -0
- package/dist/cjs/index.cjs +18 -0
- package/dist/cjs/modules/Manager.cjs +369 -0
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +96 -0
- package/dist/cjs/modules/canvas.cjs +374 -0
- package/dist/cjs/modules/index.cjs +8 -0
- package/dist/cjs/modules/storage.cjs +22 -0
- package/dist/cjs/types/api.cjs +1 -0
- package/dist/cjs/types/client.cjs +1 -0
- package/dist/cjs/types/contract.cjs +1 -0
- package/dist/cjs/types/index.cjs +7 -0
- package/dist/cjs/types/manager.cjs +11 -0
- package/dist/cjs/utils/index.cjs +4 -0
- package/dist/cjs/utils/utils.cjs +6 -0
- package/package.json +12 -11
- package/vite.cjs.config.ts +6 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const errors = require("../api/errors.cjs");
|
|
4
|
+
const api$1 = require("@polkadot/api");
|
|
5
|
+
const api = require("@prosopo/api");
|
|
6
|
+
const contract = require("@prosopo/contract");
|
|
7
|
+
const rpcProvider = require("@polkadot/rpc-provider");
|
|
8
|
+
const util$1 = require("@prosopo/util");
|
|
9
|
+
const utilCrypto = require("@polkadot/util-crypto");
|
|
10
|
+
const utils = require("../utils/utils.cjs");
|
|
11
|
+
const util = require("@polkadot/util");
|
|
12
|
+
const common = require("@prosopo/common");
|
|
13
|
+
const ExtensionWeb2 = require("../api/ExtensionWeb2.cjs");
|
|
14
|
+
const ExtensionWeb3 = require("../api/ExtensionWeb3.cjs");
|
|
15
|
+
const ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
|
|
16
|
+
const storage = require("./storage.cjs");
|
|
17
|
+
const defaultState = () => {
|
|
18
|
+
return {
|
|
19
|
+
// note order matters! see buildUpdateState. These fields are set in order, so disable modal first, then set loading to false, etc.
|
|
20
|
+
showModal: false,
|
|
21
|
+
loading: false,
|
|
22
|
+
challenge: void 0,
|
|
23
|
+
solutions: [],
|
|
24
|
+
index: -1,
|
|
25
|
+
isHuman: false,
|
|
26
|
+
captchaApi: void 0,
|
|
27
|
+
account: void 0
|
|
28
|
+
// don't handle timeout here, this should be handled by the state management
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
const buildUpdateState = (state, onStateUpdate) => {
|
|
32
|
+
const updateCurrentState = (nextState) => {
|
|
33
|
+
Object.assign(state, nextState);
|
|
34
|
+
onStateUpdate(nextState);
|
|
35
|
+
console.log("Procaptcha state update:", nextState, "\nResult:", state);
|
|
36
|
+
};
|
|
37
|
+
return updateCurrentState;
|
|
38
|
+
};
|
|
39
|
+
const getNetwork = (config) => {
|
|
40
|
+
const network = config.networks[config.defaultEnvironment];
|
|
41
|
+
if (!network) {
|
|
42
|
+
throw new Error(`No network found for environment ${config.defaultEnvironment}`);
|
|
43
|
+
}
|
|
44
|
+
return network;
|
|
45
|
+
};
|
|
46
|
+
function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
47
|
+
const alertError = (error) => {
|
|
48
|
+
console.log(error);
|
|
49
|
+
alert(error.message);
|
|
50
|
+
};
|
|
51
|
+
const events = Object.assign(
|
|
52
|
+
{
|
|
53
|
+
onAccountNotFound: alertError,
|
|
54
|
+
onError: alertError,
|
|
55
|
+
onHuman: (output) => {
|
|
56
|
+
console.log("onHuman event triggered", output);
|
|
57
|
+
},
|
|
58
|
+
onExtensionNotFound: () => {
|
|
59
|
+
alert("No extension found");
|
|
60
|
+
},
|
|
61
|
+
onExpired: () => {
|
|
62
|
+
alert("Challenge has expired, please try again");
|
|
63
|
+
},
|
|
64
|
+
onFailed: () => {
|
|
65
|
+
alert("Captcha challenge failed. Please try again");
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
callbacks
|
|
69
|
+
);
|
|
70
|
+
const dispatchErrorEvent = (err) => {
|
|
71
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
72
|
+
if (error instanceof errors.AccountNotFoundError) {
|
|
73
|
+
events.onAccountNotFound(error.message);
|
|
74
|
+
} else {
|
|
75
|
+
events.onError(error);
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const updateState = buildUpdateState(state, onStateUpdate);
|
|
79
|
+
const getConfig = () => {
|
|
80
|
+
const config = {
|
|
81
|
+
userAccountAddress: "",
|
|
82
|
+
...configOptional
|
|
83
|
+
};
|
|
84
|
+
if (state.account) {
|
|
85
|
+
config.userAccountAddress = state.account.account.address;
|
|
86
|
+
}
|
|
87
|
+
return config;
|
|
88
|
+
};
|
|
89
|
+
const fallable = async (fn) => {
|
|
90
|
+
try {
|
|
91
|
+
await fn();
|
|
92
|
+
} catch (err) {
|
|
93
|
+
console.error(err);
|
|
94
|
+
dispatchErrorEvent(err);
|
|
95
|
+
updateState({ isHuman: false, showModal: false, loading: false });
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const start = async () => {
|
|
99
|
+
console.log("Starting procaptcha");
|
|
100
|
+
await fallable(async () => {
|
|
101
|
+
if (state.loading) {
|
|
102
|
+
console.log("Procaptcha already loading");
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (state.isHuman) {
|
|
106
|
+
console.log("already human");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
resetState();
|
|
110
|
+
updateState({ loading: true });
|
|
111
|
+
const config = getConfig();
|
|
112
|
+
updateState({ dappAccount: config.account.address });
|
|
113
|
+
await utils.sleep(100);
|
|
114
|
+
const account = await loadAccount();
|
|
115
|
+
const contract$1 = await loadContract();
|
|
116
|
+
let contractIsHuman = false;
|
|
117
|
+
try {
|
|
118
|
+
contractIsHuman = (await contract$1.query.dappOperatorIsHumanUser(account.account.address, config.solutionThreshold)).value.unwrap().unwrap();
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.warn(error);
|
|
121
|
+
}
|
|
122
|
+
if (contractIsHuman) {
|
|
123
|
+
updateState({ isHuman: true, loading: false });
|
|
124
|
+
events.onHuman({
|
|
125
|
+
user: account.account.address,
|
|
126
|
+
dapp: config.account.address
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
const providerUrlFromStorage = storage.getProviderUrl();
|
|
131
|
+
let providerApi;
|
|
132
|
+
if (providerUrlFromStorage) {
|
|
133
|
+
providerApi = await loadProviderApi(providerUrlFromStorage);
|
|
134
|
+
try {
|
|
135
|
+
const verifyDappUserResponse = await providerApi.verifyDappUser(account.account.address);
|
|
136
|
+
if (verifyDappUserResponse.solutionApproved) {
|
|
137
|
+
updateState({ isHuman: true, loading: false });
|
|
138
|
+
events.onHuman({
|
|
139
|
+
providerUrl: providerUrlFromStorage,
|
|
140
|
+
user: account.account.address,
|
|
141
|
+
dapp: config.account.address,
|
|
142
|
+
commitmentId: verifyDappUserResponse.commitmentId
|
|
143
|
+
});
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
} catch (err) {
|
|
147
|
+
console.error("Error contacting provider from storage", providerUrlFromStorage);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const payload = {
|
|
151
|
+
address: account.account.address,
|
|
152
|
+
data: util.stringToU8a("message"),
|
|
153
|
+
type: "bytes"
|
|
154
|
+
};
|
|
155
|
+
const signed = await account.extension.signer.signRaw(payload);
|
|
156
|
+
console.log("Signature:", signed);
|
|
157
|
+
const getRandomProviderResponse = await contract.wrapQuery(
|
|
158
|
+
contract$1.query.getRandomActiveProvider,
|
|
159
|
+
contract$1.query
|
|
160
|
+
)(account.account.address, config.account.address);
|
|
161
|
+
const blockNumber = getRandomProviderResponse.blockNumber;
|
|
162
|
+
console.log("provider", getRandomProviderResponse);
|
|
163
|
+
const providerUrl = common.trimProviderUrl(getRandomProviderResponse.provider.url.toString());
|
|
164
|
+
providerApi = await loadProviderApi(providerUrl);
|
|
165
|
+
console.log("providerApi", providerApi);
|
|
166
|
+
const captchaApi = await loadCaptchaApi(contract$1, getRandomProviderResponse, providerApi);
|
|
167
|
+
console.log("captchaApi", captchaApi);
|
|
168
|
+
const challenge = await captchaApi.getCaptchaChallenge();
|
|
169
|
+
console.log("challenge", challenge);
|
|
170
|
+
if (challenge.captchas.length <= 0) {
|
|
171
|
+
throw new Error("No captchas returned from provider");
|
|
172
|
+
}
|
|
173
|
+
const timeMillis = challenge.captchas.map((captcha) => captcha.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b);
|
|
174
|
+
const timeout = setTimeout(() => {
|
|
175
|
+
console.log("challenge expired after " + timeMillis + "ms");
|
|
176
|
+
events.onExpired();
|
|
177
|
+
updateState({ isHuman: false, showModal: false, loading: false });
|
|
178
|
+
}, timeMillis);
|
|
179
|
+
updateState({
|
|
180
|
+
index: 0,
|
|
181
|
+
solutions: challenge.captchas.map(() => []),
|
|
182
|
+
challenge,
|
|
183
|
+
showModal: true,
|
|
184
|
+
timeout,
|
|
185
|
+
blockNumber
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
};
|
|
189
|
+
const submit = async () => {
|
|
190
|
+
await fallable(async () => {
|
|
191
|
+
console.log("submitting solutions");
|
|
192
|
+
clearTimeout();
|
|
193
|
+
if (!state.challenge) {
|
|
194
|
+
throw new Error("cannot submit, no challenge found");
|
|
195
|
+
}
|
|
196
|
+
updateState({ showModal: false });
|
|
197
|
+
const challenge = state.challenge;
|
|
198
|
+
const salt = utilCrypto.randomAsHex();
|
|
199
|
+
const captchaSolution = state.challenge.captchas.map((captcha, index) => {
|
|
200
|
+
const solution = util$1.at(state.solutions, index);
|
|
201
|
+
return {
|
|
202
|
+
captchaId: captcha.captcha.captchaId,
|
|
203
|
+
captchaContentId: captcha.captcha.captchaContentId,
|
|
204
|
+
salt,
|
|
205
|
+
solution
|
|
206
|
+
};
|
|
207
|
+
});
|
|
208
|
+
const account = getAccount();
|
|
209
|
+
const blockNumber = getBlockNumber();
|
|
210
|
+
const signer = account.extension.signer;
|
|
211
|
+
const first = util$1.at(challenge.captchas, 0);
|
|
212
|
+
if (!first.captcha.datasetId) {
|
|
213
|
+
throw new Error("No datasetId set for challenge");
|
|
214
|
+
}
|
|
215
|
+
const captchaApi = getCaptchaApi();
|
|
216
|
+
const submission = await captchaApi.submitCaptchaSolution(
|
|
217
|
+
signer,
|
|
218
|
+
challenge.requestHash,
|
|
219
|
+
first.captcha.datasetId,
|
|
220
|
+
captchaSolution,
|
|
221
|
+
salt
|
|
222
|
+
);
|
|
223
|
+
const isHuman = submission[0].solutionApproved;
|
|
224
|
+
if (!isHuman) {
|
|
225
|
+
events.onFailed();
|
|
226
|
+
}
|
|
227
|
+
updateState({
|
|
228
|
+
submission,
|
|
229
|
+
isHuman,
|
|
230
|
+
loading: false
|
|
231
|
+
});
|
|
232
|
+
if (state.isHuman) {
|
|
233
|
+
const trimmedUrl = common.trimProviderUrl(captchaApi.provider.provider.url.toString());
|
|
234
|
+
storage.setProviderUrl(trimmedUrl);
|
|
235
|
+
events.onHuman({
|
|
236
|
+
providerUrl: trimmedUrl,
|
|
237
|
+
user: account.account.address,
|
|
238
|
+
dapp: getDappAccount(),
|
|
239
|
+
commitmentId: submission[1],
|
|
240
|
+
blockNumber
|
|
241
|
+
});
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
};
|
|
245
|
+
const cancel = async () => {
|
|
246
|
+
console.log("cancel");
|
|
247
|
+
clearTimeout();
|
|
248
|
+
resetState();
|
|
249
|
+
};
|
|
250
|
+
const select = (hash) => {
|
|
251
|
+
if (!state.challenge) {
|
|
252
|
+
throw new Error("cannot select, no challenge found");
|
|
253
|
+
}
|
|
254
|
+
if (state.index >= state.challenge.captchas.length || state.index < 0) {
|
|
255
|
+
throw new Error("cannot select, round index out of range");
|
|
256
|
+
}
|
|
257
|
+
const index = state.index;
|
|
258
|
+
const solutions = state.solutions;
|
|
259
|
+
const solution = util$1.at(solutions, index);
|
|
260
|
+
if (solution.includes(hash)) {
|
|
261
|
+
console.log("deselecting", hash);
|
|
262
|
+
solution.splice(solution.indexOf(hash), 1);
|
|
263
|
+
} else {
|
|
264
|
+
console.log("selecting", hash);
|
|
265
|
+
solution.push(hash);
|
|
266
|
+
}
|
|
267
|
+
updateState({ solutions });
|
|
268
|
+
};
|
|
269
|
+
const nextRound = () => {
|
|
270
|
+
if (!state.challenge) {
|
|
271
|
+
throw new Error("cannot proceed to next round, no challenge found");
|
|
272
|
+
}
|
|
273
|
+
if (state.index + 1 >= state.challenge.captchas.length) {
|
|
274
|
+
throw new Error("cannot proceed to next round, already at last round");
|
|
275
|
+
}
|
|
276
|
+
console.log("proceeding to next round");
|
|
277
|
+
updateState({ index: state.index + 1 });
|
|
278
|
+
};
|
|
279
|
+
const loadCaptchaApi = async (contract2, provider, providerApi) => {
|
|
280
|
+
const config = getConfig();
|
|
281
|
+
const captchaApi = new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
282
|
+
getAccount().account.address,
|
|
283
|
+
contract2,
|
|
284
|
+
provider,
|
|
285
|
+
providerApi,
|
|
286
|
+
config.web2,
|
|
287
|
+
config.account.address
|
|
288
|
+
);
|
|
289
|
+
updateState({ captchaApi });
|
|
290
|
+
return getCaptchaApi();
|
|
291
|
+
};
|
|
292
|
+
const loadProviderApi = async (providerUrl) => {
|
|
293
|
+
const config = getConfig();
|
|
294
|
+
const network = getNetwork(config);
|
|
295
|
+
return new api.ProviderApi(network, providerUrl, config.account.address);
|
|
296
|
+
};
|
|
297
|
+
const clearTimeout = () => {
|
|
298
|
+
window.clearTimeout(state.timeout);
|
|
299
|
+
updateState({ timeout: void 0 });
|
|
300
|
+
};
|
|
301
|
+
const resetState = () => {
|
|
302
|
+
clearTimeout();
|
|
303
|
+
updateState(defaultState());
|
|
304
|
+
};
|
|
305
|
+
const getCaptchaApi = () => {
|
|
306
|
+
if (!state.captchaApi) {
|
|
307
|
+
throw new Error("Captcha api not set");
|
|
308
|
+
}
|
|
309
|
+
return state.captchaApi;
|
|
310
|
+
};
|
|
311
|
+
const loadAccount = async () => {
|
|
312
|
+
const config = getConfig();
|
|
313
|
+
if (!config.web2 && !config.userAccountAddress) {
|
|
314
|
+
throw new Error("Account address has not been set for web3 mode");
|
|
315
|
+
}
|
|
316
|
+
const ext = config.web2 ? new ExtensionWeb2() : new ExtensionWeb3();
|
|
317
|
+
const account = await ext.getAccount(config);
|
|
318
|
+
storage.setAccount(account.account.address);
|
|
319
|
+
console.log("Using account:", account);
|
|
320
|
+
updateState({ account });
|
|
321
|
+
return getAccount();
|
|
322
|
+
};
|
|
323
|
+
const getAccount = () => {
|
|
324
|
+
if (!state.account) {
|
|
325
|
+
throw new Error("Account not loaded");
|
|
326
|
+
}
|
|
327
|
+
const account = state.account;
|
|
328
|
+
return account;
|
|
329
|
+
};
|
|
330
|
+
const getDappAccount = () => {
|
|
331
|
+
if (!state.dappAccount) {
|
|
332
|
+
throw new Error("Dapp account not loaded");
|
|
333
|
+
}
|
|
334
|
+
const dappAccount = state.dappAccount;
|
|
335
|
+
return dappAccount;
|
|
336
|
+
};
|
|
337
|
+
const getBlockNumber = () => {
|
|
338
|
+
if (!state.blockNumber) {
|
|
339
|
+
throw new Error("Account not loaded");
|
|
340
|
+
}
|
|
341
|
+
const blockNumber = state.blockNumber;
|
|
342
|
+
return blockNumber;
|
|
343
|
+
};
|
|
344
|
+
const loadContract = async () => {
|
|
345
|
+
const config = getConfig();
|
|
346
|
+
const network = getNetwork(config);
|
|
347
|
+
const api2 = await api$1.ApiPromise.create({ provider: new rpcProvider.WsProvider(network.endpoint) });
|
|
348
|
+
const type = "sr25519";
|
|
349
|
+
const keyring = new api$1.Keyring({ type, ss58Format: api2.registry.chainSS58 });
|
|
350
|
+
return new contract.ProsopoCaptchaContract(
|
|
351
|
+
api2,
|
|
352
|
+
contract.abiJson,
|
|
353
|
+
network.contract.address,
|
|
354
|
+
keyring.addFromAddress(getAccount().account.address),
|
|
355
|
+
"prosopo",
|
|
356
|
+
0
|
|
357
|
+
);
|
|
358
|
+
};
|
|
359
|
+
return {
|
|
360
|
+
start,
|
|
361
|
+
cancel,
|
|
362
|
+
submit,
|
|
363
|
+
select,
|
|
364
|
+
nextRound
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
exports.Manager = Manager;
|
|
368
|
+
exports.defaultState = defaultState;
|
|
369
|
+
exports.getNetwork = getNetwork;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
+
require("../datasets/dist/index.cjs");
|
|
4
|
+
const handlers = require("../api/handlers.cjs");
|
|
5
|
+
const common = require("@prosopo/common");
|
|
6
|
+
const util = require("@prosopo/util");
|
|
7
|
+
const util$1 = require("@polkadot/util");
|
|
8
|
+
const merkle = require("../datasets/dist/captcha/merkle.cjs");
|
|
9
|
+
const captcha = require("../datasets/dist/captcha/captcha.cjs");
|
|
10
|
+
class ProsopoCaptchaApi {
|
|
11
|
+
constructor(userAccount, contract, provider, providerApi, web2, dappAccount) {
|
|
12
|
+
this.userAccount = userAccount;
|
|
13
|
+
this.contract = contract;
|
|
14
|
+
this.provider = provider;
|
|
15
|
+
this.providerApi = providerApi;
|
|
16
|
+
this.web2 = web2;
|
|
17
|
+
this.dappAccount = dappAccount;
|
|
18
|
+
}
|
|
19
|
+
async getCaptchaChallenge() {
|
|
20
|
+
try {
|
|
21
|
+
const captchaChallenge = await this.providerApi.getCaptchaChallenge(this.userAccount, this.provider);
|
|
22
|
+
this.verifyCaptchaChallengeContent(this.provider, captchaChallenge);
|
|
23
|
+
return captchaChallenge;
|
|
24
|
+
} catch (e) {
|
|
25
|
+
throw new common.ProsopoEnvError(e);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
verifyCaptchaChallengeContent(provider, captchaChallenge) {
|
|
29
|
+
const first = util.at(captchaChallenge.captchas, 0);
|
|
30
|
+
const proofLength = first.proof.length;
|
|
31
|
+
console.log(provider.provider);
|
|
32
|
+
const last = util.at(first.proof, proofLength - 1);
|
|
33
|
+
if (provider.provider.datasetIdContent.toString() !== util.at(last, 0)) {
|
|
34
|
+
throw new common.ProsopoEnvError("CAPTCHA.INVALID_DATASET_CONTENT_ID");
|
|
35
|
+
}
|
|
36
|
+
for (const captchaWithProof of captchaChallenge.captchas) {
|
|
37
|
+
if (!verifyCaptchaData(captchaWithProof)) {
|
|
38
|
+
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
39
|
+
}
|
|
40
|
+
if (!merkle.verifyProof(captchaWithProof.captcha.captchaContentId, captchaWithProof.proof)) {
|
|
41
|
+
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
console.log("CAPTCHA.CHALLENGE_VERIFIED");
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
async submitCaptchaSolution(signer, requestHash, datasetId, solutions, salt) {
|
|
48
|
+
const tree = new merkle.CaptchaMerkleTree();
|
|
49
|
+
const captchasHashed = solutions.map((captcha$1) => captcha.computeCaptchaSolutionHash(captcha$1));
|
|
50
|
+
tree.build(captchasHashed);
|
|
51
|
+
const commitmentId = tree.root.hash;
|
|
52
|
+
console.log("solveCaptchaChallenge commitmentId", commitmentId);
|
|
53
|
+
const tx = void 0;
|
|
54
|
+
let signature = void 0;
|
|
55
|
+
if (this.web2) {
|
|
56
|
+
if (!signer || !signer.signRaw) {
|
|
57
|
+
throw new Error("Signer is not defined, cannot sign message to prove account ownership");
|
|
58
|
+
}
|
|
59
|
+
const signed = await signer.signRaw({
|
|
60
|
+
address: this.userAccount,
|
|
61
|
+
data: util$1.stringToHex(requestHash),
|
|
62
|
+
type: "bytes"
|
|
63
|
+
});
|
|
64
|
+
signature = signed.signature;
|
|
65
|
+
}
|
|
66
|
+
let result;
|
|
67
|
+
try {
|
|
68
|
+
result = await this.providerApi.submitCaptchaSolution(
|
|
69
|
+
solutions,
|
|
70
|
+
requestHash,
|
|
71
|
+
this.contract.pair.address,
|
|
72
|
+
salt,
|
|
73
|
+
signature
|
|
74
|
+
);
|
|
75
|
+
} catch (err) {
|
|
76
|
+
throw new handlers.ProsopoApiError(err);
|
|
77
|
+
}
|
|
78
|
+
return [result, commitmentId, tx];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
async function verifyCaptchaData(captchaWithProof) {
|
|
82
|
+
const captcha$1 = captchaWithProof.captcha;
|
|
83
|
+
const proof = captchaWithProof.proof;
|
|
84
|
+
if (!(await Promise.all(captcha$1.items.map(async (item) => (await captcha.computeItemHash(item)).hash === item.hash))).every(
|
|
85
|
+
(hash) => hash === true
|
|
86
|
+
)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const captchaHash = captcha.computeCaptchaHash(captcha$1, false, false, false);
|
|
90
|
+
if (captchaHash !== captcha$1.captchaContentId) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return util.at(proof, 0).indexOf(captchaHash) !== -1;
|
|
94
|
+
}
|
|
95
|
+
exports.ProsopoCaptchaApi = ProsopoCaptchaApi;
|
|
96
|
+
exports.default = ProsopoCaptchaApi;
|