@prosopo/procaptcha 0.2.13 → 0.2.15
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/Extension.d.ts +0 -10
- package/dist/api/Extension.d.ts.map +1 -1
- package/dist/api/Extension.js +0 -3
- package/dist/api/Extension.js.map +1 -1
- package/dist/api/ExtensionWeb2.d.ts +0 -3
- package/dist/api/ExtensionWeb2.d.ts.map +1 -1
- package/dist/api/ExtensionWeb2.js +9 -17
- package/dist/api/ExtensionWeb2.js.map +1 -1
- package/dist/api/ExtensionWeb3.d.ts +0 -3
- package/dist/api/ExtensionWeb3.d.ts.map +1 -1
- package/dist/api/ExtensionWeb3.js +0 -5
- package/dist/api/ExtensionWeb3.js.map +1 -1
- package/dist/api/errors.js +0 -13
- package/dist/api/errors.js.map +1 -1
- package/dist/api/handlers.js +0 -15
- package/dist/api/handlers.js.map +1 -1
- package/dist/api/index.js +0 -13
- package/dist/api/index.js.map +1 -1
- package/dist/api/sign.js +2 -15
- package/dist/api/sign.js.map +1 -1
- package/dist/cjs/api/ExtensionWeb2.cjs +17 -16
- package/dist/cjs/modules/Manager.cjs +217 -212
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +10 -12
- package/dist/index.js +0 -13
- package/dist/index.js.map +1 -1
- package/dist/modules/Manager.d.ts +0 -3
- package/dist/modules/Manager.d.ts.map +1 -1
- package/dist/modules/Manager.js +195 -287
- 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 -28
- package/dist/modules/ProsopoCaptchaApi.js.map +1 -1
- package/dist/modules/canvas.js +0 -14
- package/dist/modules/canvas.js.map +1 -1
- package/dist/modules/index.js +0 -13
- package/dist/modules/index.js.map +1 -1
- package/dist/modules/storage.d.ts +0 -12
- package/dist/modules/storage.d.ts.map +1 -1
- package/dist/modules/storage.js +0 -25
- package/dist/modules/storage.js.map +1 -1
- package/dist/tests/mocks/browser.js +0 -13
- package/dist/tests/mocks/browser.js.map +1 -1
- package/dist/types/api.js +0 -19
- package/dist/types/api.js.map +1 -1
- package/dist/types/index.js +0 -13
- package/dist/types/index.js.map +1 -1
- package/dist/types/manager.d.ts +0 -24
- package/dist/types/manager.d.ts.map +1 -1
- package/dist/utils/index.js +0 -13
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/utils.js +0 -13
- package/dist/utils/utils.js.map +1 -1
- package/package.json +10 -8
- 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,135 @@ 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(
|
|
150
|
+
account.account.address,
|
|
151
|
+
void 0,
|
|
152
|
+
configOptional.challengeValidLength
|
|
153
|
+
);
|
|
112
154
|
if (verifyDappUserResponse.solutionApproved) {
|
|
113
|
-
|
|
155
|
+
updateState({ isHuman: true, loading: false });
|
|
156
|
+
events.onHuman({
|
|
157
|
+
providerUrl: providerUrlFromStorage,
|
|
158
|
+
user: account.account.address,
|
|
159
|
+
dapp: getDappAccount(),
|
|
160
|
+
commitmentId: verifyDappUserResponse.commitmentId
|
|
161
|
+
});
|
|
162
|
+
setValidChallengeTimeout();
|
|
114
163
|
return;
|
|
115
164
|
}
|
|
116
165
|
} catch (err) {
|
|
117
166
|
console.error("Error contacting provider from storage", providerUrlFromStorage);
|
|
118
167
|
}
|
|
119
168
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
169
|
+
const payload = {
|
|
170
|
+
address: account.account.address,
|
|
171
|
+
data: string.stringToU8a("message"),
|
|
172
|
+
type: "bytes"
|
|
173
|
+
};
|
|
174
|
+
const signed = await account.extension.signer.signRaw(payload);
|
|
175
|
+
console.log("Signature:", signed);
|
|
176
|
+
const getRandomProviderResponse = await contract.wrapQuery(
|
|
177
|
+
contract$1.query.getRandomActiveProvider,
|
|
178
|
+
contract$1.query
|
|
179
|
+
)(account.account.address, getDappAccount());
|
|
180
|
+
const blockNumber = parseInt(getRandomProviderResponse.blockNumber.toString());
|
|
181
|
+
console.log("provider", getRandomProviderResponse);
|
|
182
|
+
const providerUrl = common.trimProviderUrl(getRandomProviderResponse.provider.url.toString());
|
|
183
|
+
providerApi = await loadProviderApi(providerUrl);
|
|
184
|
+
console.log("providerApi", providerApi);
|
|
185
|
+
const captchaApi = await loadCaptchaApi(contract$1, getRandomProviderResponse, providerApi);
|
|
186
|
+
console.log("captchaApi", captchaApi);
|
|
187
|
+
const challenge = await captchaApi.getCaptchaChallenge();
|
|
188
|
+
console.log("challenge", challenge);
|
|
122
189
|
if (challenge.captchas.length <= 0) {
|
|
123
190
|
throw new Error("No captchas returned from provider");
|
|
124
191
|
}
|
|
192
|
+
const timeMillis = challenge.captchas.map((captcha) => captcha.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b);
|
|
193
|
+
const timeout = setTimeout(() => {
|
|
194
|
+
console.log("challenge expired after " + timeMillis + "ms");
|
|
195
|
+
events.onChallengeExpired();
|
|
196
|
+
updateState({ isHuman: false, showModal: false, loading: false });
|
|
197
|
+
}, timeMillis);
|
|
125
198
|
updateState({
|
|
126
|
-
challenge,
|
|
127
199
|
index: 0,
|
|
128
200
|
solutions: challenge.captchas.map(() => []),
|
|
201
|
+
challenge,
|
|
129
202
|
showModal: true,
|
|
130
|
-
timeout
|
|
131
|
-
blockNumber
|
|
203
|
+
timeout,
|
|
204
|
+
blockNumber
|
|
132
205
|
});
|
|
133
206
|
});
|
|
134
207
|
};
|
|
135
208
|
const submit = async () => {
|
|
136
209
|
await fallable(async () => {
|
|
210
|
+
console.log("submitting solutions");
|
|
137
211
|
clearTimeout();
|
|
138
|
-
updateState({ showModal: false });
|
|
139
212
|
if (!state.challenge) {
|
|
140
213
|
throw new Error("cannot submit, no challenge found");
|
|
141
214
|
}
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
state.
|
|
147
|
-
|
|
148
|
-
|
|
215
|
+
updateState({ showModal: false });
|
|
216
|
+
const challenge = state.challenge;
|
|
217
|
+
const salt = random.randomAsHex();
|
|
218
|
+
const captchaSolution = state.challenge.captchas.map((captcha, index) => {
|
|
219
|
+
const solution = util.at(state.solutions, index);
|
|
220
|
+
return {
|
|
221
|
+
captchaId: captcha.captcha.captchaId,
|
|
222
|
+
captchaContentId: captcha.captcha.captchaContentId,
|
|
223
|
+
salt,
|
|
224
|
+
solution
|
|
225
|
+
};
|
|
226
|
+
});
|
|
227
|
+
const account = getAccount();
|
|
228
|
+
const blockNumber = getBlockNumber();
|
|
229
|
+
const signer = account.extension.signer;
|
|
230
|
+
const first = util.at(challenge.captchas, 0);
|
|
231
|
+
if (!first.captcha.datasetId) {
|
|
232
|
+
throw new Error("No datasetId set for challenge");
|
|
233
|
+
}
|
|
234
|
+
const captchaApi = getCaptchaApi();
|
|
235
|
+
const submission = await captchaApi.submitCaptchaSolution(
|
|
236
|
+
signer,
|
|
237
|
+
challenge.requestHash,
|
|
238
|
+
first.captcha.datasetId,
|
|
239
|
+
captchaSolution,
|
|
149
240
|
salt
|
|
150
241
|
);
|
|
151
242
|
const isHuman = submission[0].solutionApproved;
|
|
@@ -157,20 +248,26 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
157
248
|
isHuman,
|
|
158
249
|
loading: false
|
|
159
250
|
});
|
|
160
|
-
if (isHuman) {
|
|
161
|
-
const trimmedUrl = common.trimProviderUrl(
|
|
251
|
+
if (state.isHuman) {
|
|
252
|
+
const trimmedUrl = common.trimProviderUrl(captchaApi.provider.provider.url.toString());
|
|
162
253
|
storage.setProviderUrl(trimmedUrl);
|
|
163
254
|
events.onHuman({
|
|
164
255
|
providerUrl: trimmedUrl,
|
|
165
|
-
user:
|
|
256
|
+
user: account.account.address,
|
|
166
257
|
dapp: getDappAccount(),
|
|
167
258
|
commitmentId: submission[1],
|
|
168
|
-
blockNumber
|
|
259
|
+
blockNumber
|
|
169
260
|
});
|
|
170
261
|
setValidChallengeTimeout();
|
|
171
262
|
}
|
|
172
263
|
});
|
|
173
264
|
};
|
|
265
|
+
const cancel = async () => {
|
|
266
|
+
console.log("cancel");
|
|
267
|
+
clearTimeout();
|
|
268
|
+
resetState();
|
|
269
|
+
events.onClose();
|
|
270
|
+
};
|
|
174
271
|
const select = (hash) => {
|
|
175
272
|
if (!state.challenge) {
|
|
176
273
|
throw new Error("cannot select, no challenge found");
|
|
@@ -178,9 +275,17 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
178
275
|
if (state.index >= state.challenge.captchas.length || state.index < 0) {
|
|
179
276
|
throw new Error("cannot select, round index out of range");
|
|
180
277
|
}
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
278
|
+
const index = state.index;
|
|
279
|
+
const solutions = state.solutions;
|
|
280
|
+
const solution = util.at(solutions, index);
|
|
281
|
+
if (solution.includes(hash)) {
|
|
282
|
+
console.log("deselecting", hash);
|
|
283
|
+
solution.splice(solution.indexOf(hash), 1);
|
|
284
|
+
} else {
|
|
285
|
+
console.log("selecting", hash);
|
|
286
|
+
solution.push(hash);
|
|
287
|
+
}
|
|
288
|
+
updateState({ solutions });
|
|
184
289
|
};
|
|
185
290
|
const nextRound = () => {
|
|
186
291
|
if (!state.challenge) {
|
|
@@ -189,161 +294,44 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
189
294
|
if (state.index + 1 >= state.challenge.captchas.length) {
|
|
190
295
|
throw new Error("cannot proceed to next round, already at last round");
|
|
191
296
|
}
|
|
297
|
+
console.log("proceeding to next round");
|
|
192
298
|
updateState({ index: state.index + 1 });
|
|
193
299
|
};
|
|
194
300
|
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
301
|
const config = getConfig();
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
302
|
+
const captchaApi = new ProsopoCaptchaApi.ProsopoCaptchaApi(
|
|
303
|
+
getAccount().account.address,
|
|
304
|
+
contract2,
|
|
305
|
+
provider,
|
|
306
|
+
providerApi,
|
|
307
|
+
config.web2,
|
|
308
|
+
getDappAccount()
|
|
309
|
+
);
|
|
310
|
+
updateState({ captchaApi });
|
|
311
|
+
return getCaptchaApi();
|
|
226
312
|
};
|
|
227
313
|
const loadProviderApi = async (providerUrl) => {
|
|
228
314
|
const config = getConfig();
|
|
315
|
+
const network = getNetwork(config);
|
|
229
316
|
if (!config.account.address) {
|
|
230
317
|
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
231
318
|
}
|
|
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();
|
|
319
|
+
return new api.ProviderApi(network, providerUrl, config.account.address);
|
|
342
320
|
};
|
|
343
321
|
const clearTimeout = () => {
|
|
344
322
|
window.clearTimeout(state.timeout);
|
|
345
323
|
updateState({ timeout: void 0 });
|
|
346
324
|
};
|
|
325
|
+
const setValidChallengeTimeout = () => {
|
|
326
|
+
console.log("setting valid challenge timeout");
|
|
327
|
+
const timeMillis = configOptional.challengeValidLength || 120 * 1e3;
|
|
328
|
+
const successfullChallengeTimeout = setTimeout(() => {
|
|
329
|
+
console.log("valid challenge expired after " + timeMillis + "ms");
|
|
330
|
+
updateState({ isHuman: false });
|
|
331
|
+
events.onExpired();
|
|
332
|
+
}, timeMillis);
|
|
333
|
+
updateState({ successfullChallengeTimeout });
|
|
334
|
+
};
|
|
347
335
|
const resetState = () => {
|
|
348
336
|
clearTimeout();
|
|
349
337
|
updateState(defaultState());
|
|
@@ -354,37 +342,54 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
354
342
|
}
|
|
355
343
|
return state.captchaApi;
|
|
356
344
|
};
|
|
345
|
+
const loadAccount = async () => {
|
|
346
|
+
const config = getConfig();
|
|
347
|
+
if (!config.web2 && !config.userAccountAddress) {
|
|
348
|
+
throw new Error("Account address has not been set for web3 mode");
|
|
349
|
+
}
|
|
350
|
+
const ext = config.web2 ? new ExtensionWeb2() : new ExtensionWeb3();
|
|
351
|
+
const account = await ext.getAccount(config);
|
|
352
|
+
storage.setAccount(account.account.address);
|
|
353
|
+
console.log("Using account:", account);
|
|
354
|
+
updateState({ account });
|
|
355
|
+
return getAccount();
|
|
356
|
+
};
|
|
357
357
|
const getAccount = () => {
|
|
358
358
|
if (!state.account) {
|
|
359
359
|
throw new Error("Account not loaded");
|
|
360
360
|
}
|
|
361
|
-
|
|
361
|
+
const account = state.account;
|
|
362
|
+
return account;
|
|
362
363
|
};
|
|
363
364
|
const getDappAccount = () => {
|
|
364
365
|
if (!state.dappAccount) {
|
|
365
366
|
throw new common.ProsopoEnvError("GENERAL.SITE_KEY_MISSING");
|
|
366
367
|
}
|
|
367
|
-
|
|
368
|
+
const dappAccount = state.dappAccount;
|
|
369
|
+
return dappAccount;
|
|
368
370
|
};
|
|
369
|
-
const
|
|
371
|
+
const getBlockNumber = () => {
|
|
370
372
|
if (!state.blockNumber) {
|
|
371
373
|
throw new Error("Account not loaded");
|
|
372
374
|
}
|
|
373
|
-
|
|
375
|
+
const blockNumber = state.blockNumber;
|
|
376
|
+
return blockNumber;
|
|
377
|
+
};
|
|
378
|
+
const loadContract = async () => {
|
|
379
|
+
const config = getConfig();
|
|
380
|
+
const network = getNetwork(config);
|
|
381
|
+
const api2 = await Api.ApiPromise.create({ provider: new ws.WsProvider(network.endpoint), initWasm: false });
|
|
382
|
+
const type = "sr25519";
|
|
383
|
+
const keyring$1 = new keyring.Keyring({ type, ss58Format: api2.registry.chainSS58 });
|
|
384
|
+
return new contract.ProsopoCaptchaContract(
|
|
385
|
+
api2,
|
|
386
|
+
JSON.parse(contractInfo.ContractAbi),
|
|
387
|
+
network.contract.address,
|
|
388
|
+
"prosopo",
|
|
389
|
+
0,
|
|
390
|
+
keyring$1.addFromAddress(getAccount().account.address)
|
|
391
|
+
);
|
|
374
392
|
};
|
|
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
393
|
return {
|
|
389
394
|
start,
|
|
390
395
|
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;
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,3 @@
|
|
|
1
|
-
// Copyright 2021-2023 Prosopo (UK) Ltd.
|
|
2
|
-
//
|
|
3
|
-
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
-
// you may not use this file except in compliance with the License.
|
|
5
|
-
// You may obtain a copy of the License at
|
|
6
|
-
//
|
|
7
|
-
// http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
-
//
|
|
9
|
-
// Unless required by applicable law or agreed to in writing, software
|
|
10
|
-
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
-
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
-
// See the License for the specific language governing permissions and
|
|
13
|
-
// limitations under the License.
|
|
14
1
|
export * from './api/index.js';
|
|
15
2
|
export * from './modules/index.js';
|
|
16
3
|
export * from './types/index.js';
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAaA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA"}
|
|
@@ -10,9 +10,6 @@ export declare const getNetwork: (config: ProcaptchaClientConfigOutput) => {
|
|
|
10
10
|
pairType: "sr25519" | "ed25519" | "ecdsa" | "ethereum";
|
|
11
11
|
ss58Format: number;
|
|
12
12
|
};
|
|
13
|
-
/**
|
|
14
|
-
* The state operator. This is used to mutate the state of Procaptcha during the captcha process. State updates are published via the onStateUpdate callback. This should be used by frontends, e.g. react, to maintain the state of Procaptcha across renders.
|
|
15
|
-
*/
|
|
16
13
|
export declare function Manager(configOptional: ProcaptchaConfigOptional, state: ProcaptchaState, onStateUpdate: ProcaptchaStateUpdateFn, callbacks: ProcaptchaCallbacks): {
|
|
17
14
|
start: () => Promise<void>;
|
|
18
15
|
cancel: () => Promise<void>;
|
|
@@ -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;AAKD,wBAAgB,OAAO,CACnB,cAAc,EAAE,wBAAwB,EACxC,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,mBAAmB;;;;mBAuTR,MAAM;;EA+K/B"}
|