@prosopo/procaptcha 0.2.13 → 0.2.14
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/ExtensionWeb2.d.ts.map +1 -1
- package/dist/api/ExtensionWeb2.js +9 -8
- package/dist/api/ExtensionWeb2.js.map +1 -1
- package/dist/api/sign.js +2 -2
- package/dist/api/sign.js.map +1 -1
- package/dist/cjs/api/ExtensionWeb2.cjs +17 -16
- package/dist/cjs/modules/Manager.cjs +213 -212
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +10 -12
- package/dist/modules/Manager.d.ts.map +1 -1
- package/dist/modules/Manager.js +252 -267
- package/dist/modules/Manager.js.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.d.ts +1 -1
- package/dist/modules/ProsopoCaptchaApi.d.ts.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.js +1 -1
- package/dist/modules/ProsopoCaptchaApi.js.map +1 -1
- package/package.json +7 -7
- package/dist/cjs/contracts/captcha/dist/build-extrinsic/captcha.cjs +0 -339
- package/dist/cjs/contracts/captcha/dist/contract-info/captcha.cjs +0 -6
- package/dist/cjs/contracts/captcha/dist/contracts/captcha.cjs +0 -79
- package/dist/cjs/contracts/captcha/dist/data/captcha.json.cjs +0 -3374
- package/dist/cjs/contracts/captcha/dist/event-data/captcha.json.cjs +0 -3
- package/dist/cjs/contracts/captcha/dist/events/captcha.cjs +0 -23
- package/dist/cjs/contracts/captcha/dist/index.cjs +0 -44
- package/dist/cjs/contracts/captcha/dist/mixed-methods/captcha.cjs +0 -470
- package/dist/cjs/contracts/captcha/dist/query/captcha.cjs +0 -468
- package/dist/cjs/contracts/captcha/dist/shared/utils.cjs +0 -31
- package/dist/cjs/contracts/captcha/dist/tx-sign-and-send/captcha.cjs +0 -426
- package/dist/cjs/contracts/captcha/dist/types-arguments/captcha.cjs +0 -62
- package/dist/cjs/packages/datasets/dist/captcha/captcha.cjs +0 -143
- package/dist/cjs/packages/datasets/dist/captcha/dataset.cjs +0 -87
- package/dist/cjs/packages/datasets/dist/captcha/index.cjs +0 -27
- package/dist/cjs/packages/datasets/dist/captcha/merkle.cjs +0 -106
- package/dist/cjs/packages/datasets/dist/captcha/util.cjs +0 -16
- package/dist/cjs/packages/datasets/dist/index.cjs +0 -2
|
@@ -1,34 +1,43 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const errors = require("../api/errors.cjs");
|
|
4
|
-
const
|
|
4
|
+
const Api = require("@polkadot/api/promise/Api");
|
|
5
5
|
const types = require("@prosopo/types");
|
|
6
|
-
const
|
|
6
|
+
const api = require("@prosopo/api");
|
|
7
|
+
const keyring = require("@polkadot/keyring");
|
|
7
8
|
const contract = require("@prosopo/contract");
|
|
8
9
|
const common = require("@prosopo/common");
|
|
9
|
-
const
|
|
10
|
-
require("
|
|
10
|
+
const ws = require("@polkadot/rpc-provider/ws");
|
|
11
|
+
const contractInfo = require("@prosopo/captcha-contract/contract-info");
|
|
11
12
|
const util = require("@prosopo/util");
|
|
12
|
-
const
|
|
13
|
-
const
|
|
13
|
+
const random = require("@polkadot/util-crypto/random");
|
|
14
|
+
const utils = require("../utils/utils.cjs");
|
|
15
|
+
const string = require("@polkadot/util/string");
|
|
14
16
|
const ExtensionWeb2 = require("../api/ExtensionWeb2.cjs");
|
|
15
17
|
const ExtensionWeb3 = require("../api/ExtensionWeb3.cjs");
|
|
16
18
|
const ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
|
|
17
19
|
const storage = require("./storage.cjs");
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
const defaultState = () => {
|
|
21
|
+
return {
|
|
22
|
+
// note order matters! see buildUpdateState. These fields are set in order, so disable modal first, then set loading to false, etc.
|
|
23
|
+
showModal: false,
|
|
24
|
+
loading: false,
|
|
25
|
+
index: 0,
|
|
26
|
+
challenge: void 0,
|
|
27
|
+
solutions: void 0,
|
|
28
|
+
isHuman: false,
|
|
29
|
+
captchaApi: void 0,
|
|
30
|
+
account: void 0
|
|
31
|
+
// don't handle timeout here, this should be handled by the state management
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
const buildUpdateState = (state, onStateUpdate) => {
|
|
35
|
+
const updateCurrentState = (nextState) => {
|
|
36
|
+
Object.assign(state, nextState);
|
|
37
|
+
onStateUpdate(nextState);
|
|
38
|
+
console.log("Procaptcha state update:", nextState, "\nResult:", state);
|
|
39
|
+
};
|
|
40
|
+
return updateCurrentState;
|
|
32
41
|
};
|
|
33
42
|
const getNetwork = (config) => {
|
|
34
43
|
const network = config.networks[config.defaultNetwork];
|
|
@@ -39,7 +48,7 @@ const getNetwork = (config) => {
|
|
|
39
48
|
};
|
|
40
49
|
function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
41
50
|
const alertError = (error) => {
|
|
42
|
-
console.
|
|
51
|
+
console.log(error);
|
|
43
52
|
alert(error.message);
|
|
44
53
|
};
|
|
45
54
|
const events = Object.assign(
|
|
@@ -47,7 +56,7 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
47
56
|
onAccountNotFound: alertError,
|
|
48
57
|
onError: alertError,
|
|
49
58
|
onHuman: (output) => {
|
|
50
|
-
console.
|
|
59
|
+
console.log("onHuman event triggered", output);
|
|
51
60
|
},
|
|
52
61
|
onExtensionNotFound: () => {
|
|
53
62
|
alert("No extension found");
|
|
@@ -62,10 +71,10 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
62
71
|
alert("Uncompleted challenge has expired, please try again");
|
|
63
72
|
},
|
|
64
73
|
onOpen: () => {
|
|
65
|
-
console.
|
|
74
|
+
console.log("onOpen event triggered");
|
|
66
75
|
},
|
|
67
76
|
onClose: () => {
|
|
68
|
-
console.
|
|
77
|
+
console.log("onClose event triggered");
|
|
69
78
|
}
|
|
70
79
|
},
|
|
71
80
|
callbacks
|
|
@@ -79,10 +88,16 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
79
88
|
}
|
|
80
89
|
};
|
|
81
90
|
const updateState = buildUpdateState(state, onStateUpdate);
|
|
82
|
-
const getConfig = () =>
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
91
|
+
const getConfig = () => {
|
|
92
|
+
const config = {
|
|
93
|
+
userAccountAddress: "",
|
|
94
|
+
...configOptional
|
|
95
|
+
};
|
|
96
|
+
if (state.account) {
|
|
97
|
+
config.userAccountAddress = state.account.account.address;
|
|
98
|
+
}
|
|
99
|
+
return types.ProcaptchaConfigSchema.parse(config);
|
|
100
|
+
};
|
|
86
101
|
const fallable = async (fn) => {
|
|
87
102
|
try {
|
|
88
103
|
await fn();
|
|
@@ -93,59 +108,131 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
93
108
|
}
|
|
94
109
|
};
|
|
95
110
|
const start = async () => {
|
|
111
|
+
console.log("Starting procaptcha");
|
|
96
112
|
events.onOpen();
|
|
97
113
|
await fallable(async () => {
|
|
98
|
-
if (state.loading
|
|
114
|
+
if (state.loading) {
|
|
115
|
+
console.log("Procaptcha already loading");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (state.isHuman) {
|
|
119
|
+
console.log("already human");
|
|
99
120
|
return;
|
|
121
|
+
}
|
|
100
122
|
resetState();
|
|
101
|
-
updateState({
|
|
123
|
+
updateState({ loading: true });
|
|
124
|
+
const config = getConfig();
|
|
125
|
+
updateState({ dappAccount: config.account.address });
|
|
126
|
+
await utils.sleep(100);
|
|
102
127
|
const account = await loadAccount();
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
128
|
+
const contract$1 = await loadContract();
|
|
129
|
+
let contractIsHuman = false;
|
|
130
|
+
try {
|
|
131
|
+
contractIsHuman = (await contract$1.query.dappOperatorIsHumanUser(account.account.address, config.solutionThreshold)).value.unwrap().unwrap();
|
|
132
|
+
} catch (error) {
|
|
133
|
+
console.warn(error);
|
|
134
|
+
}
|
|
135
|
+
if (contractIsHuman) {
|
|
136
|
+
updateState({ isHuman: true, loading: false });
|
|
137
|
+
events.onHuman({
|
|
138
|
+
user: account.account.address,
|
|
139
|
+
dapp: getDappAccount()
|
|
140
|
+
});
|
|
141
|
+
setValidChallengeTimeout();
|
|
106
142
|
return;
|
|
107
143
|
}
|
|
108
144
|
const providerUrlFromStorage = storage.getProviderUrl();
|
|
145
|
+
let providerApi;
|
|
109
146
|
if (providerUrlFromStorage) {
|
|
147
|
+
providerApi = await loadProviderApi(providerUrlFromStorage);
|
|
110
148
|
try {
|
|
111
|
-
const verifyDappUserResponse = await
|
|
149
|
+
const verifyDappUserResponse = await providerApi.verifyDappUser(account.account.address);
|
|
112
150
|
if (verifyDappUserResponse.solutionApproved) {
|
|
113
|
-
|
|
151
|
+
updateState({ isHuman: true, loading: false });
|
|
152
|
+
events.onHuman({
|
|
153
|
+
providerUrl: providerUrlFromStorage,
|
|
154
|
+
user: account.account.address,
|
|
155
|
+
dapp: getDappAccount(),
|
|
156
|
+
commitmentId: verifyDappUserResponse.commitmentId
|
|
157
|
+
});
|
|
158
|
+
setValidChallengeTimeout();
|
|
114
159
|
return;
|
|
115
160
|
}
|
|
116
161
|
} catch (err) {
|
|
117
162
|
console.error("Error contacting provider from storage", providerUrlFromStorage);
|
|
118
163
|
}
|
|
119
164
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
165
|
+
const payload = {
|
|
166
|
+
address: account.account.address,
|
|
167
|
+
data: string.stringToU8a("message"),
|
|
168
|
+
type: "bytes"
|
|
169
|
+
};
|
|
170
|
+
const signed = await account.extension.signer.signRaw(payload);
|
|
171
|
+
console.log("Signature:", signed);
|
|
172
|
+
const getRandomProviderResponse = await contract.wrapQuery(
|
|
173
|
+
contract$1.query.getRandomActiveProvider,
|
|
174
|
+
contract$1.query
|
|
175
|
+
)(account.account.address, getDappAccount());
|
|
176
|
+
const blockNumber = parseInt(getRandomProviderResponse.blockNumber.toString());
|
|
177
|
+
console.log("provider", getRandomProviderResponse);
|
|
178
|
+
const providerUrl = common.trimProviderUrl(getRandomProviderResponse.provider.url.toString());
|
|
179
|
+
providerApi = await loadProviderApi(providerUrl);
|
|
180
|
+
console.log("providerApi", providerApi);
|
|
181
|
+
const captchaApi = await loadCaptchaApi(contract$1, getRandomProviderResponse, providerApi);
|
|
182
|
+
console.log("captchaApi", captchaApi);
|
|
183
|
+
const challenge = await captchaApi.getCaptchaChallenge();
|
|
184
|
+
console.log("challenge", challenge);
|
|
122
185
|
if (challenge.captchas.length <= 0) {
|
|
123
186
|
throw new Error("No captchas returned from provider");
|
|
124
187
|
}
|
|
188
|
+
const timeMillis = challenge.captchas.map((captcha) => captcha.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b);
|
|
189
|
+
const timeout = setTimeout(() => {
|
|
190
|
+
console.log("challenge expired after " + timeMillis + "ms");
|
|
191
|
+
events.onChallengeExpired();
|
|
192
|
+
updateState({ isHuman: false, showModal: false, loading: false });
|
|
193
|
+
}, timeMillis);
|
|
125
194
|
updateState({
|
|
126
|
-
challenge,
|
|
127
195
|
index: 0,
|
|
128
196
|
solutions: challenge.captchas.map(() => []),
|
|
197
|
+
challenge,
|
|
129
198
|
showModal: true,
|
|
130
|
-
timeout
|
|
131
|
-
blockNumber
|
|
199
|
+
timeout,
|
|
200
|
+
blockNumber
|
|
132
201
|
});
|
|
133
202
|
});
|
|
134
203
|
};
|
|
135
204
|
const submit = async () => {
|
|
136
205
|
await fallable(async () => {
|
|
206
|
+
console.log("submitting solutions");
|
|
137
207
|
clearTimeout();
|
|
138
|
-
updateState({ showModal: false });
|
|
139
208
|
if (!state.challenge) {
|
|
140
209
|
throw new Error("cannot submit, no challenge found");
|
|
141
210
|
}
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
state.
|
|
147
|
-
|
|
148
|
-
|
|
211
|
+
updateState({ showModal: false });
|
|
212
|
+
const challenge = state.challenge;
|
|
213
|
+
const salt = random.randomAsHex();
|
|
214
|
+
const captchaSolution = state.challenge.captchas.map((captcha, index) => {
|
|
215
|
+
const solution = util.at(state.solutions, index);
|
|
216
|
+
return {
|
|
217
|
+
captchaId: captcha.captcha.captchaId,
|
|
218
|
+
captchaContentId: captcha.captcha.captchaContentId,
|
|
219
|
+
salt,
|
|
220
|
+
solution
|
|
221
|
+
};
|
|
222
|
+
});
|
|
223
|
+
const account = getAccount();
|
|
224
|
+
const blockNumber = getBlockNumber();
|
|
225
|
+
const signer = account.extension.signer;
|
|
226
|
+
const first = util.at(challenge.captchas, 0);
|
|
227
|
+
if (!first.captcha.datasetId) {
|
|
228
|
+
throw new Error("No datasetId set for challenge");
|
|
229
|
+
}
|
|
230
|
+
const captchaApi = getCaptchaApi();
|
|
231
|
+
const submission = await captchaApi.submitCaptchaSolution(
|
|
232
|
+
signer,
|
|
233
|
+
challenge.requestHash,
|
|
234
|
+
first.captcha.datasetId,
|
|
235
|
+
captchaSolution,
|
|
149
236
|
salt
|
|
150
237
|
);
|
|
151
238
|
const isHuman = submission[0].solutionApproved;
|
|
@@ -157,20 +244,26 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
157
244
|
isHuman,
|
|
158
245
|
loading: false
|
|
159
246
|
});
|
|
160
|
-
if (isHuman) {
|
|
161
|
-
const trimmedUrl = common.trimProviderUrl(
|
|
247
|
+
if (state.isHuman) {
|
|
248
|
+
const trimmedUrl = common.trimProviderUrl(captchaApi.provider.provider.url.toString());
|
|
162
249
|
storage.setProviderUrl(trimmedUrl);
|
|
163
250
|
events.onHuman({
|
|
164
251
|
providerUrl: trimmedUrl,
|
|
165
|
-
user:
|
|
252
|
+
user: account.account.address,
|
|
166
253
|
dapp: getDappAccount(),
|
|
167
254
|
commitmentId: submission[1],
|
|
168
|
-
blockNumber
|
|
255
|
+
blockNumber
|
|
169
256
|
});
|
|
170
257
|
setValidChallengeTimeout();
|
|
171
258
|
}
|
|
172
259
|
});
|
|
173
260
|
};
|
|
261
|
+
const cancel = async () => {
|
|
262
|
+
console.log("cancel");
|
|
263
|
+
clearTimeout();
|
|
264
|
+
resetState();
|
|
265
|
+
events.onClose();
|
|
266
|
+
};
|
|
174
267
|
const select = (hash) => {
|
|
175
268
|
if (!state.challenge) {
|
|
176
269
|
throw new Error("cannot select, no challenge found");
|
|
@@ -178,9 +271,17 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
178
271
|
if (state.index >= state.challenge.captchas.length || state.index < 0) {
|
|
179
272
|
throw new Error("cannot select, round index out of range");
|
|
180
273
|
}
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
274
|
+
const index = state.index;
|
|
275
|
+
const solutions = state.solutions;
|
|
276
|
+
const solution = util.at(solutions, index);
|
|
277
|
+
if (solution.includes(hash)) {
|
|
278
|
+
console.log("deselecting", hash);
|
|
279
|
+
solution.splice(solution.indexOf(hash), 1);
|
|
280
|
+
} else {
|
|
281
|
+
console.log("selecting", hash);
|
|
282
|
+
solution.push(hash);
|
|
283
|
+
}
|
|
284
|
+
updateState({ solutions });
|
|
184
285
|
};
|
|
185
286
|
const nextRound = () => {
|
|
186
287
|
if (!state.challenge) {
|
|
@@ -189,161 +290,44 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
189
290
|
if (state.index + 1 >= state.challenge.captchas.length) {
|
|
190
291
|
throw new Error("cannot proceed to next round, already at last round");
|
|
191
292
|
}
|
|
293
|
+
console.log("proceeding to next round");
|
|
192
294
|
updateState({ index: state.index + 1 });
|
|
193
295
|
};
|
|
194
296
|
const loadCaptchaApi = async (contract2, provider, providerApi) => {
|
|
195
|
-
updateState({
|
|
196
|
-
captchaApi: new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
197
|
-
getAccount().account.address,
|
|
198
|
-
contract2,
|
|
199
|
-
provider,
|
|
200
|
-
providerApi,
|
|
201
|
-
getConfig().web2,
|
|
202
|
-
getDappAccount()
|
|
203
|
-
)
|
|
204
|
-
});
|
|
205
|
-
return getCaptchaApi();
|
|
206
|
-
};
|
|
207
|
-
const createBlockObservable = () => new rxjs.Observable(
|
|
208
|
-
(subscriber) => () => api.ApiPromise.create({ provider: new api.WsProvider(getNetwork(getConfig()).endpoint) }).then((api2) => {
|
|
209
|
-
api2.rpc.chain.subscribeNewHeads((header) => {
|
|
210
|
-
subscriber.next(header);
|
|
211
|
-
});
|
|
212
|
-
}).catch((error) => {
|
|
213
|
-
subscriber.error(error);
|
|
214
|
-
})
|
|
215
|
-
);
|
|
216
|
-
const loadAccount = async () => {
|
|
217
297
|
const config = getConfig();
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
298
|
+
const captchaApi = new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
299
|
+
getAccount().account.address,
|
|
300
|
+
contract2,
|
|
301
|
+
provider,
|
|
302
|
+
providerApi,
|
|
303
|
+
config.web2,
|
|
304
|
+
getDappAccount()
|
|
305
|
+
);
|
|
306
|
+
updateState({ captchaApi });
|
|
307
|
+
return getCaptchaApi();
|
|
226
308
|
};
|
|
227
309
|
const loadProviderApi = async (providerUrl) => {
|
|
228
310
|
const config = getConfig();
|
|
311
|
+
const network = getNetwork(config);
|
|
229
312
|
if (!config.account.address) {
|
|
230
313
|
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
231
314
|
}
|
|
232
|
-
return new api
|
|
233
|
-
};
|
|
234
|
-
const loadContract = async () => {
|
|
235
|
-
const network = getNetwork(getConfig());
|
|
236
|
-
const api$12 = await api.ApiPromise.create({ provider: new api.WsProvider(network.endpoint) });
|
|
237
|
-
const type = "sr25519";
|
|
238
|
-
return new contract.ProsopoCaptchaContract(
|
|
239
|
-
api$12,
|
|
240
|
-
JSON.parse(captcha.ContractAbi),
|
|
241
|
-
network.contract.address,
|
|
242
|
-
"prosopo",
|
|
243
|
-
0,
|
|
244
|
-
new api.Keyring({ type, ss58Format: api$12.registry.chainSS58 }).addFromAddress(getAccount().account.address)
|
|
245
|
-
);
|
|
246
|
-
};
|
|
247
|
-
function handleIsSelected(solution, hash) {
|
|
248
|
-
if (solution.includes(hash)) {
|
|
249
|
-
solution.splice(solution.indexOf(hash), 1);
|
|
250
|
-
} else {
|
|
251
|
-
solution.push(hash);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
function getSolutionsFromState(salt) {
|
|
255
|
-
if (!state.challenge) {
|
|
256
|
-
throw new Error("cannot get solutions, no challenge found");
|
|
257
|
-
}
|
|
258
|
-
return state.challenge.captchas.map((captcha2, index) => ({
|
|
259
|
-
captchaId: captcha2.captcha.captchaId,
|
|
260
|
-
captchaContentId: captcha2.captcha.captchaContentId,
|
|
261
|
-
salt,
|
|
262
|
-
solution: util.at(state.solutions, index)
|
|
263
|
-
}));
|
|
264
|
-
}
|
|
265
|
-
function handleHumanInCachedProvider(providerUrlFromStorage, account, verifyDappUserResponse) {
|
|
266
|
-
updateState({ isHuman: true, loading: false });
|
|
267
|
-
events.onHuman({
|
|
268
|
-
providerUrl: providerUrlFromStorage,
|
|
269
|
-
user: account.account.address,
|
|
270
|
-
dapp: getDappAccount(),
|
|
271
|
-
commitmentId: verifyDappUserResponse.commitmentId
|
|
272
|
-
});
|
|
273
|
-
setValidChallengeTimeout();
|
|
274
|
-
}
|
|
275
|
-
function getVerifyDappUserFunction(providerUrlFromStorage, account) {
|
|
276
|
-
return loadProviderApi(providerUrlFromStorage).then(
|
|
277
|
-
(providerApi) => providerApi.verifyDappUser(account.account.address)
|
|
278
|
-
);
|
|
279
|
-
}
|
|
280
|
-
function handleHumanInContract(account) {
|
|
281
|
-
updateState({ isHuman: true, loading: false });
|
|
282
|
-
events.onHuman({
|
|
283
|
-
user: account.account.address,
|
|
284
|
-
dapp: getDappAccount()
|
|
285
|
-
});
|
|
286
|
-
setValidChallengeTimeout();
|
|
287
|
-
}
|
|
288
|
-
async function checkHumanInContract(contract2, account) {
|
|
289
|
-
try {
|
|
290
|
-
return await contract2.query.dappOperatorIsHumanUser(account.account.address, getConfig().solutionThreshold).then((res) => res.value.unwrap().unwrap());
|
|
291
|
-
} catch (err) {
|
|
292
|
-
console.error(err);
|
|
293
|
-
return false;
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
function getChallenge(getRandomProviderResponse2, contract2) {
|
|
297
|
-
return loadProviderApi(common.trimProviderUrl(getRandomProviderResponse2.provider.url.toString())).then((api2) => loadCaptchaApi(contract2, getRandomProviderResponse2, api2)).then((captchaApi) => captchaApi.getCaptchaChallenge());
|
|
298
|
-
}
|
|
299
|
-
function getRandomProviderResponse(contract$1, account) {
|
|
300
|
-
return rxjs.lastValueFrom(
|
|
301
|
-
rxjs.from(
|
|
302
|
-
contract.wrapQuery(contract$1.query.getRandomActiveProvider, contract$1.query)(
|
|
303
|
-
account.account.address,
|
|
304
|
-
getDappAccount()
|
|
305
|
-
)
|
|
306
|
-
).pipe(
|
|
307
|
-
operators.retry({
|
|
308
|
-
count: 3,
|
|
309
|
-
delay: (error, retryCount) => {
|
|
310
|
-
console.error(`Attempt ${retryCount} failed. Retrying on next block. Error: ${error}`);
|
|
311
|
-
return createBlockObservable().pipe(operators.take(1));
|
|
312
|
-
},
|
|
313
|
-
resetOnSuccess: true
|
|
314
|
-
})
|
|
315
|
-
)
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
function setTimeToComplete(challenge) {
|
|
319
|
-
return setTimeout(
|
|
320
|
-
() => {
|
|
321
|
-
events.onChallengeExpired();
|
|
322
|
-
updateState({ isHuman: false, showModal: false, loading: false });
|
|
323
|
-
},
|
|
324
|
-
challenge.captchas.map((captcha2) => captcha2.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b)
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
const setValidChallengeTimeout = () => {
|
|
328
|
-
updateState({
|
|
329
|
-
successfullChallengeTimeout: setTimeout(
|
|
330
|
-
() => {
|
|
331
|
-
updateState({ isHuman: false });
|
|
332
|
-
events.onExpired();
|
|
333
|
-
},
|
|
334
|
-
configOptional.challengeValidLength || 120 * 1e3
|
|
335
|
-
)
|
|
336
|
-
});
|
|
337
|
-
};
|
|
338
|
-
const cancel = async () => {
|
|
339
|
-
clearTimeout();
|
|
340
|
-
resetState();
|
|
341
|
-
events.onClose();
|
|
315
|
+
return new api.ProviderApi(network, providerUrl, config.account.address);
|
|
342
316
|
};
|
|
343
317
|
const clearTimeout = () => {
|
|
344
318
|
window.clearTimeout(state.timeout);
|
|
345
319
|
updateState({ timeout: void 0 });
|
|
346
320
|
};
|
|
321
|
+
const setValidChallengeTimeout = () => {
|
|
322
|
+
console.log("setting valid challenge timeout");
|
|
323
|
+
const timeMillis = configOptional.challengeValidLength || 120 * 1e3;
|
|
324
|
+
const successfullChallengeTimeout = setTimeout(() => {
|
|
325
|
+
console.log("valid challenge expired after " + timeMillis + "ms");
|
|
326
|
+
updateState({ isHuman: false });
|
|
327
|
+
events.onExpired();
|
|
328
|
+
}, timeMillis);
|
|
329
|
+
updateState({ successfullChallengeTimeout });
|
|
330
|
+
};
|
|
347
331
|
const resetState = () => {
|
|
348
332
|
clearTimeout();
|
|
349
333
|
updateState(defaultState());
|
|
@@ -354,37 +338,54 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
354
338
|
}
|
|
355
339
|
return state.captchaApi;
|
|
356
340
|
};
|
|
341
|
+
const loadAccount = async () => {
|
|
342
|
+
const config = getConfig();
|
|
343
|
+
if (!config.web2 && !config.userAccountAddress) {
|
|
344
|
+
throw new Error("Account address has not been set for web3 mode");
|
|
345
|
+
}
|
|
346
|
+
const ext = config.web2 ? new ExtensionWeb2() : new ExtensionWeb3();
|
|
347
|
+
const account = await ext.getAccount(config);
|
|
348
|
+
storage.setAccount(account.account.address);
|
|
349
|
+
console.log("Using account:", account);
|
|
350
|
+
updateState({ account });
|
|
351
|
+
return getAccount();
|
|
352
|
+
};
|
|
357
353
|
const getAccount = () => {
|
|
358
354
|
if (!state.account) {
|
|
359
355
|
throw new Error("Account not loaded");
|
|
360
356
|
}
|
|
361
|
-
|
|
357
|
+
const account = state.account;
|
|
358
|
+
return account;
|
|
362
359
|
};
|
|
363
360
|
const getDappAccount = () => {
|
|
364
361
|
if (!state.dappAccount) {
|
|
365
362
|
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
366
363
|
}
|
|
367
|
-
|
|
364
|
+
const dappAccount = state.dappAccount;
|
|
365
|
+
return dappAccount;
|
|
368
366
|
};
|
|
369
|
-
const
|
|
367
|
+
const getBlockNumber = () => {
|
|
370
368
|
if (!state.blockNumber) {
|
|
371
369
|
throw new Error("Account not loaded");
|
|
372
370
|
}
|
|
373
|
-
|
|
371
|
+
const blockNumber = state.blockNumber;
|
|
372
|
+
return blockNumber;
|
|
373
|
+
};
|
|
374
|
+
const loadContract = async () => {
|
|
375
|
+
const config = getConfig();
|
|
376
|
+
const network = getNetwork(config);
|
|
377
|
+
const api2 = await Api.ApiPromise.create({ provider: new ws.WsProvider(network.endpoint), initWasm: false });
|
|
378
|
+
const type = "sr25519";
|
|
379
|
+
const keyring$1 = new keyring.Keyring({ type, ss58Format: api2.registry.chainSS58 });
|
|
380
|
+
return new contract.ProsopoCaptchaContract(
|
|
381
|
+
api2,
|
|
382
|
+
JSON.parse(contractInfo.ContractAbi),
|
|
383
|
+
network.contract.address,
|
|
384
|
+
"prosopo",
|
|
385
|
+
0,
|
|
386
|
+
keyring$1.addFromAddress(getAccount().account.address)
|
|
387
|
+
);
|
|
374
388
|
};
|
|
375
|
-
function getBlockNumberFromProvider(getRandomProviderResponse2) {
|
|
376
|
-
return parseInt(getRandomProviderResponse2.blockNumber.toString());
|
|
377
|
-
}
|
|
378
|
-
function getDatasetId() {
|
|
379
|
-
if (!state.challenge) {
|
|
380
|
-
throw new Error("cannot get datasetId, no challenge found");
|
|
381
|
-
}
|
|
382
|
-
const datasetId = util.at(state.challenge.captchas, 0).captcha.datasetId;
|
|
383
|
-
if (!datasetId) {
|
|
384
|
-
throw new Error("No datasetId set for challenge");
|
|
385
|
-
}
|
|
386
|
-
return datasetId;
|
|
387
|
-
}
|
|
388
389
|
return {
|
|
389
390
|
start,
|
|
390
391
|
cancel,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
-
require("
|
|
3
|
+
const datasets = require("@prosopo/datasets");
|
|
4
4
|
const handlers = require("../api/handlers.cjs");
|
|
5
5
|
const common = require("@prosopo/common");
|
|
6
6
|
const util = require("@prosopo/util");
|
|
7
|
-
const
|
|
8
|
-
const merkle = require("../packages/datasets/dist/captcha/merkle.cjs");
|
|
9
|
-
const captcha = require("../packages/datasets/dist/captcha/captcha.cjs");
|
|
7
|
+
const string = require("@polkadot/util/string");
|
|
10
8
|
class ProsopoCaptchaApi {
|
|
11
9
|
constructor(userAccount, contract, provider, providerApi, web2, dappAccount) {
|
|
12
10
|
this.userAccount = userAccount;
|
|
@@ -37,7 +35,7 @@ class ProsopoCaptchaApi {
|
|
|
37
35
|
if (!verifyCaptchaData(captchaWithProof)) {
|
|
38
36
|
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
39
37
|
}
|
|
40
|
-
if (!
|
|
38
|
+
if (!datasets.verifyProof(captchaWithProof.captcha.captchaContentId, captchaWithProof.proof)) {
|
|
41
39
|
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
42
40
|
}
|
|
43
41
|
}
|
|
@@ -45,8 +43,8 @@ class ProsopoCaptchaApi {
|
|
|
45
43
|
return;
|
|
46
44
|
}
|
|
47
45
|
async submitCaptchaSolution(signer, requestHash, datasetId, solutions, salt) {
|
|
48
|
-
const tree = new
|
|
49
|
-
const captchasHashed = solutions.map((captcha
|
|
46
|
+
const tree = new datasets.CaptchaMerkleTree();
|
|
47
|
+
const captchasHashed = solutions.map((captcha) => datasets.computeCaptchaSolutionHash(captcha));
|
|
50
48
|
tree.build(captchasHashed);
|
|
51
49
|
const commitmentId = tree.root.hash;
|
|
52
50
|
console.log("solveCaptchaChallenge commitmentId", commitmentId);
|
|
@@ -58,7 +56,7 @@ class ProsopoCaptchaApi {
|
|
|
58
56
|
}
|
|
59
57
|
const signed = await signer.signRaw({
|
|
60
58
|
address: this.userAccount,
|
|
61
|
-
data:
|
|
59
|
+
data: string.stringToHex(requestHash),
|
|
62
60
|
type: "bytes"
|
|
63
61
|
});
|
|
64
62
|
signature = signed.signature;
|
|
@@ -79,15 +77,15 @@ class ProsopoCaptchaApi {
|
|
|
79
77
|
}
|
|
80
78
|
}
|
|
81
79
|
async function verifyCaptchaData(captchaWithProof) {
|
|
82
|
-
const captcha
|
|
80
|
+
const captcha = captchaWithProof.captcha;
|
|
83
81
|
const proof = captchaWithProof.proof;
|
|
84
|
-
if (!(await Promise.all(captcha
|
|
82
|
+
if (!(await Promise.all(captcha.items.map(async (item) => (await datasets.computeItemHash(item)).hash === item.hash))).every(
|
|
85
83
|
(hash) => hash === true
|
|
86
84
|
)) {
|
|
87
85
|
return false;
|
|
88
86
|
}
|
|
89
|
-
const captchaHash =
|
|
90
|
-
if (captchaHash !== captcha
|
|
87
|
+
const captchaHash = datasets.computeCaptchaHash(captcha, false, false, false);
|
|
88
|
+
if (captchaHash !== captcha.captchaContentId) {
|
|
91
89
|
return false;
|
|
92
90
|
}
|
|
93
91
|
return util.at(proof, 0).indexOf(captchaHash) !== -1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Manager.d.ts","sourceRoot":"","sources":["../../src/modules/Manager.ts"],"names":[],"mappings":"AAaA,OAAO,EAEH,mBAAmB,EACnB,wBAAwB,EAExB,eAAe,EACf,uBAAuB,EAC1B,MAAM,qBAAqB,CAAA;AAG5B,OAAO,
|
|
1
|
+
{"version":3,"file":"Manager.d.ts","sourceRoot":"","sources":["../../src/modules/Manager.ts"],"names":[],"mappings":"AAaA,OAAO,EAEH,mBAAmB,EACnB,wBAAwB,EAExB,eAAe,EACf,uBAAuB,EAC1B,MAAM,qBAAqB,CAAA;AAG5B,OAAO,EAGH,4BAA4B,EAE/B,MAAM,gBAAgB,CAAA;AAmBvB,eAAO,MAAM,YAAY,QAAO,QAAQ,eAAe,CAatD,CAAA;AAgBD,eAAO,MAAM,UAAU,WAAY,4BAA4B;;;;;;;;CAM9D,CAAA;AAED;;GAEG;AACH,wBAAgB,OAAO,CACnB,cAAc,EAAE,wBAAwB,EACxC,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,mBAAmB;;;;mBAmTR,MAAM;;EA+K/B"}
|