@prosopo/procaptcha 0.2.25 → 0.2.32
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/cjs/contracts/captcha/dist/contract-info/captcha.cjs +6 -0
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/modules/Manager.cjs +8 -26
- package/dist/cjs/modules/ProsopoCaptchaApi.cjs +10 -8
- package/dist/cjs/modules/index.cjs +0 -2
- package/dist/cjs/packages/datasets/dist/captcha/captcha.cjs +143 -0
- package/dist/cjs/packages/datasets/dist/captcha/dataset.cjs +87 -0
- package/dist/cjs/packages/datasets/dist/captcha/index.cjs +27 -0
- package/dist/cjs/packages/datasets/dist/captcha/merkle.cjs +106 -0
- package/dist/cjs/packages/datasets/dist/captcha/util.cjs +16 -0
- package/dist/cjs/packages/datasets/dist/index.cjs +2 -0
- package/dist/modules/Manager.d.ts.map +1 -1
- package/dist/modules/Manager.js +29 -13
- package/dist/modules/Manager.js.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.d.ts.map +1 -1
- package/dist/modules/ProsopoCaptchaApi.js +15 -7
- package/dist/modules/ProsopoCaptchaApi.js.map +1 -1
- package/dist/modules/canvas.d.ts.map +1 -1
- package/dist/modules/canvas.js +3 -4
- package/dist/modules/canvas.js.map +1 -1
- package/package.json +7 -7
- package/dist/cjs/modules/collector.cjs +0 -60
package/dist/cjs/index.cjs
CHANGED
|
@@ -6,13 +6,11 @@ require("./types/index.cjs");
|
|
|
6
6
|
require("./utils/index.cjs");
|
|
7
7
|
const Manager = require("./modules/Manager.cjs");
|
|
8
8
|
const ProsopoCaptchaApi = require("./modules/ProsopoCaptchaApi.cjs");
|
|
9
|
-
const collector = require("./modules/collector.cjs");
|
|
10
9
|
const manager = require("./types/manager.cjs");
|
|
11
10
|
const utils = require("./utils/utils.cjs");
|
|
12
11
|
exports.Manager = Manager.Manager;
|
|
13
12
|
exports.defaultState = Manager.defaultState;
|
|
14
13
|
exports.getNetwork = Manager.getNetwork;
|
|
15
14
|
exports.ProsopoCaptchaApi = ProsopoCaptchaApi.ProsopoCaptchaApi;
|
|
16
|
-
exports.startCollector = collector.startCollector;
|
|
17
15
|
exports.ProcapchaEventNames = manager.ProcapchaEventNames;
|
|
18
16
|
exports.sleep = utils.sleep;
|
|
@@ -8,7 +8,7 @@ const keyring = require("@polkadot/keyring");
|
|
|
8
8
|
const contract = require("@prosopo/contract");
|
|
9
9
|
const common = require("@prosopo/common");
|
|
10
10
|
const ws = require("@polkadot/rpc-provider/ws");
|
|
11
|
-
const
|
|
11
|
+
const captcha = require("../contracts/captcha/dist/contract-info/captcha.cjs");
|
|
12
12
|
const util = require("@prosopo/util");
|
|
13
13
|
const random = require("@polkadot/util-crypto/random");
|
|
14
14
|
const utils = require("../utils/utils.cjs");
|
|
@@ -57,14 +57,12 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
57
57
|
onError: alertError,
|
|
58
58
|
onHuman: (output) => {
|
|
59
59
|
console.log("onHuman event triggered", output);
|
|
60
|
-
updateState({ sendData: !state.sendData });
|
|
61
60
|
},
|
|
62
61
|
onExtensionNotFound: () => {
|
|
63
62
|
alert("No extension found");
|
|
64
63
|
},
|
|
65
64
|
onFailed: () => {
|
|
66
65
|
alert("Captcha challenge failed. Please try again");
|
|
67
|
-
updateState({ sendData: !state.sendData });
|
|
68
66
|
},
|
|
69
67
|
onExpired: () => {
|
|
70
68
|
alert("Completed challenge has expired, please try again");
|
|
@@ -74,7 +72,6 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
74
72
|
},
|
|
75
73
|
onOpen: () => {
|
|
76
74
|
console.log("onOpen event triggered");
|
|
77
|
-
updateState({ sendData: !state.sendData });
|
|
78
75
|
},
|
|
79
76
|
onClose: () => {
|
|
80
77
|
console.log("onClose event triggered");
|
|
@@ -149,12 +146,7 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
149
146
|
if (providerUrlFromStorage) {
|
|
150
147
|
providerApi = await loadProviderApi(providerUrlFromStorage);
|
|
151
148
|
try {
|
|
152
|
-
const verifyDappUserResponse = await providerApi.verifyDappUser(
|
|
153
|
-
getDappAccount(),
|
|
154
|
-
account.account.address,
|
|
155
|
-
void 0,
|
|
156
|
-
configOptional.challengeValidLength
|
|
157
|
-
);
|
|
149
|
+
const verifyDappUserResponse = await providerApi.verifyDappUser(account.account.address);
|
|
158
150
|
if (verifyDappUserResponse.solutionApproved) {
|
|
159
151
|
updateState({ isHuman: true, loading: false });
|
|
160
152
|
events.onHuman({
|
|
@@ -193,7 +185,7 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
193
185
|
if (challenge.captchas.length <= 0) {
|
|
194
186
|
throw new Error("No captchas returned from provider");
|
|
195
187
|
}
|
|
196
|
-
const timeMillis = challenge.captchas.map((
|
|
188
|
+
const timeMillis = challenge.captchas.map((captcha2) => captcha2.captcha.timeLimitMs || 30 * 1e3).reduce((a, b) => a + b);
|
|
197
189
|
const timeout = setTimeout(() => {
|
|
198
190
|
console.log("challenge expired after " + timeMillis + "ms");
|
|
199
191
|
events.onChallengeExpired();
|
|
@@ -219,11 +211,11 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
219
211
|
updateState({ showModal: false });
|
|
220
212
|
const challenge = state.challenge;
|
|
221
213
|
const salt = random.randomAsHex();
|
|
222
|
-
const captchaSolution = state.challenge.captchas.map((
|
|
214
|
+
const captchaSolution = state.challenge.captchas.map((captcha2, index) => {
|
|
223
215
|
const solution = util.at(state.solutions, index);
|
|
224
216
|
return {
|
|
225
|
-
captchaId:
|
|
226
|
-
captchaContentId:
|
|
217
|
+
captchaId: captcha2.captcha.captchaId,
|
|
218
|
+
captchaContentId: captcha2.captcha.captchaContentId,
|
|
227
219
|
salt,
|
|
228
220
|
solution
|
|
229
221
|
};
|
|
@@ -387,29 +379,19 @@ function Manager(configOptional, state, onStateUpdate, callbacks) {
|
|
|
387
379
|
const keyring$1 = new keyring.Keyring({ type, ss58Format: api2.registry.chainSS58 });
|
|
388
380
|
return new contract.ProsopoCaptchaContract(
|
|
389
381
|
api2,
|
|
390
|
-
JSON.parse(
|
|
382
|
+
JSON.parse(captcha.ContractAbi),
|
|
391
383
|
network.contract.address,
|
|
392
384
|
"prosopo",
|
|
393
385
|
0,
|
|
394
386
|
keyring$1.addFromAddress(getAccount().account.address)
|
|
395
387
|
);
|
|
396
388
|
};
|
|
397
|
-
const exportData = async (events2) => {
|
|
398
|
-
var _a;
|
|
399
|
-
const providerUrl = storage.getProviderUrl() || ((_a = state.captchaApi) == null ? void 0 : _a.provider.provider.url.toString());
|
|
400
|
-
if (!providerUrl) {
|
|
401
|
-
return;
|
|
402
|
-
}
|
|
403
|
-
const providerApi = await loadProviderApi(providerUrl);
|
|
404
|
-
await providerApi.submitUserEvents(events2, getAccount().account.address);
|
|
405
|
-
};
|
|
406
389
|
return {
|
|
407
390
|
start,
|
|
408
391
|
cancel,
|
|
409
392
|
submit,
|
|
410
393
|
select,
|
|
411
|
-
nextRound
|
|
412
|
-
exportData
|
|
394
|
+
nextRound
|
|
413
395
|
};
|
|
414
396
|
}
|
|
415
397
|
exports.Manager = Manager;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
-
|
|
3
|
+
require("../packages/datasets/dist/index.cjs");
|
|
4
4
|
const handlers = require("../api/handlers.cjs");
|
|
5
5
|
const common = require("@prosopo/common");
|
|
6
6
|
const util = require("@prosopo/util");
|
|
7
7
|
const string = require("@polkadot/util/string");
|
|
8
|
+
const merkle = require("../packages/datasets/dist/captcha/merkle.cjs");
|
|
9
|
+
const captcha = require("../packages/datasets/dist/captcha/captcha.cjs");
|
|
8
10
|
class ProsopoCaptchaApi {
|
|
9
11
|
constructor(userAccount, contract, provider, providerApi, web2, dappAccount) {
|
|
10
12
|
this.userAccount = userAccount;
|
|
@@ -35,7 +37,7 @@ class ProsopoCaptchaApi {
|
|
|
35
37
|
if (!verifyCaptchaData(captchaWithProof)) {
|
|
36
38
|
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
37
39
|
}
|
|
38
|
-
if (!
|
|
40
|
+
if (!merkle.verifyProof(captchaWithProof.captcha.captchaContentId, captchaWithProof.proof)) {
|
|
39
41
|
throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_CHALLENGE");
|
|
40
42
|
}
|
|
41
43
|
}
|
|
@@ -43,8 +45,8 @@ class ProsopoCaptchaApi {
|
|
|
43
45
|
return;
|
|
44
46
|
}
|
|
45
47
|
async submitCaptchaSolution(signer, requestHash, datasetId, solutions, salt) {
|
|
46
|
-
const tree = new
|
|
47
|
-
const captchasHashed = solutions.map((captcha) =>
|
|
48
|
+
const tree = new merkle.CaptchaMerkleTree();
|
|
49
|
+
const captchasHashed = solutions.map((captcha$1) => captcha.computeCaptchaSolutionHash(captcha$1));
|
|
48
50
|
tree.build(captchasHashed);
|
|
49
51
|
const commitmentId = tree.root.hash;
|
|
50
52
|
console.log("solveCaptchaChallenge commitmentId", commitmentId);
|
|
@@ -77,15 +79,15 @@ class ProsopoCaptchaApi {
|
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
81
|
async function verifyCaptchaData(captchaWithProof) {
|
|
80
|
-
const captcha = captchaWithProof.captcha;
|
|
82
|
+
const captcha$1 = captchaWithProof.captcha;
|
|
81
83
|
const proof = captchaWithProof.proof;
|
|
82
|
-
if (!(await Promise.all(captcha.items.map(async (item) => (await
|
|
84
|
+
if (!(await Promise.all(captcha$1.items.map(async (item) => (await captcha.computeItemHash(item)).hash === item.hash))).every(
|
|
83
85
|
(hash) => hash === true
|
|
84
86
|
)) {
|
|
85
87
|
return false;
|
|
86
88
|
}
|
|
87
|
-
const captchaHash =
|
|
88
|
-
if (captchaHash !== captcha.captchaContentId) {
|
|
89
|
+
const captchaHash = captcha.computeCaptchaHash(captcha$1, false, false, false);
|
|
90
|
+
if (captchaHash !== captcha$1.captchaContentId) {
|
|
89
91
|
return false;
|
|
90
92
|
}
|
|
91
93
|
return util.at(proof, 0).indexOf(captchaHash) !== -1;
|
|
@@ -2,9 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
3
|
const Manager = require("./Manager.cjs");
|
|
4
4
|
const ProsopoCaptchaApi = require("./ProsopoCaptchaApi.cjs");
|
|
5
|
-
const collector = require("./collector.cjs");
|
|
6
5
|
exports.Manager = Manager.Manager;
|
|
7
6
|
exports.defaultState = Manager.defaultState;
|
|
8
7
|
exports.getNetwork = Manager.getNetwork;
|
|
9
8
|
exports.ProsopoCaptchaApi = ProsopoCaptchaApi.ProsopoCaptchaApi;
|
|
10
|
-
exports.startCollector = collector.startCollector;
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const types = require("@prosopo/types");
|
|
4
|
+
const common = require("@prosopo/common");
|
|
5
|
+
const util = require("@prosopo/util");
|
|
6
|
+
const util$1 = require("./util.cjs");
|
|
7
|
+
const NO_SOLUTION_VALUE = "NO_SOLUTION";
|
|
8
|
+
function parseCaptchaDataset(datasetJSON) {
|
|
9
|
+
try {
|
|
10
|
+
const result = types.DatasetWithNumericSolutionSchema.parse(datasetJSON);
|
|
11
|
+
const result2 = {
|
|
12
|
+
format: result.format,
|
|
13
|
+
captchas: result.captchas.map((captcha) => {
|
|
14
|
+
return {
|
|
15
|
+
...captcha,
|
|
16
|
+
solution: captcha.solution ? matchItemsToSolutions(captcha.solution, captcha.items) : [],
|
|
17
|
+
unlabelled: captcha.unlabelled ? matchItemsToSolutions(captcha.unlabelled, captcha.items) : []
|
|
18
|
+
};
|
|
19
|
+
})
|
|
20
|
+
};
|
|
21
|
+
if (result.datasetId !== void 0)
|
|
22
|
+
result2.datasetId = result.datasetId;
|
|
23
|
+
if (result.contentTree !== void 0)
|
|
24
|
+
result2.contentTree = result.contentTree;
|
|
25
|
+
if (result.datasetContentId !== void 0)
|
|
26
|
+
result2.datasetContentId = result.datasetContentId;
|
|
27
|
+
if (result.solutionTree !== void 0)
|
|
28
|
+
result2.solutionTree = result.solutionTree;
|
|
29
|
+
return result2;
|
|
30
|
+
} catch (err) {
|
|
31
|
+
throw new common.ProsopoEnvError(err);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function parseAndSortCaptchaSolutions(captchaJSON) {
|
|
35
|
+
try {
|
|
36
|
+
return types.CaptchaSolutionArraySchema.parse(captchaJSON).map((captcha) => ({
|
|
37
|
+
...captcha,
|
|
38
|
+
solution: captcha.solution.sort()
|
|
39
|
+
}));
|
|
40
|
+
} catch (err) {
|
|
41
|
+
throw new common.ProsopoEnvError(err);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function captchaSort(a, b) {
|
|
45
|
+
return a.captchaId.localeCompare(b.captchaId);
|
|
46
|
+
}
|
|
47
|
+
function sortAndComputeHashes(received, stored) {
|
|
48
|
+
received.sort(captchaSort);
|
|
49
|
+
stored.sort(captchaSort);
|
|
50
|
+
return stored.map(({ salt, items = [], target = "", captchaId, solved }, index) => {
|
|
51
|
+
const item = util.at(received, index);
|
|
52
|
+
if (captchaId != item.captchaId) {
|
|
53
|
+
throw new common.ProsopoEnvError("CAPTCHA.ID_MISMATCH");
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
hash: computeCaptchaHash({
|
|
57
|
+
solution: solved ? item.solution : [],
|
|
58
|
+
salt,
|
|
59
|
+
items,
|
|
60
|
+
target
|
|
61
|
+
}, true, true, false),
|
|
62
|
+
captchaId
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
function compareCaptchaSolutions(received, stored) {
|
|
67
|
+
if (received.length && stored.length && received.length === stored.length) {
|
|
68
|
+
const hashes = sortAndComputeHashes(received, stored);
|
|
69
|
+
return hashes.every(({ hash, captchaId }) => hash === captchaId);
|
|
70
|
+
}
|
|
71
|
+
return false;
|
|
72
|
+
}
|
|
73
|
+
function computeCaptchaHash(captcha, includeSolution = false, includeSalt = false, sortItemHashes) {
|
|
74
|
+
try {
|
|
75
|
+
const itemHashes = captcha.items.map((item, index) => {
|
|
76
|
+
if (item.hash) {
|
|
77
|
+
return item.hash;
|
|
78
|
+
} else {
|
|
79
|
+
throw new common.ProsopoEnvError("CAPTCHA.MISSING_ITEM_HASH", computeCaptchaHash.name, void 0, index);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
return common.hexHashArray([
|
|
83
|
+
captcha.target,
|
|
84
|
+
// empty array hashes as empty string, undefined solution results in the array [`NO_SOLUTION`]
|
|
85
|
+
// [undefined] also hashes as empty string, which is why we don't use it
|
|
86
|
+
...includeSolution ? getSolutionValueToHash(captcha.solution) : [],
|
|
87
|
+
includeSalt ? captcha.salt : "",
|
|
88
|
+
sortItemHashes ? itemHashes.sort() : itemHashes
|
|
89
|
+
]);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
throw new common.ProsopoEnvError(err);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function getSolutionValueToHash(solution) {
|
|
95
|
+
return solution !== void 0 ? solution.sort() : [NO_SOLUTION_VALUE];
|
|
96
|
+
}
|
|
97
|
+
async function computeItemHash(item) {
|
|
98
|
+
if (item.type === "text") {
|
|
99
|
+
return { ...item, hash: common.hexHash(item.data) };
|
|
100
|
+
} else if (item.type === "image") {
|
|
101
|
+
return { ...item, hash: common.hexHash(await util$1.downloadImage(item.data)) };
|
|
102
|
+
} else {
|
|
103
|
+
throw new common.ProsopoEnvError("CAPTCHA.INVALID_ITEM_FORMAT");
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function matchItemsToSolutions(solutions, items) {
|
|
107
|
+
if (!items) {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
return solutions.map((solution) => {
|
|
111
|
+
if (typeof solution === "string") {
|
|
112
|
+
if (!(items == null ? void 0 : items.some((item2) => item2.hash === solution))) {
|
|
113
|
+
throw new common.ProsopoEnvError("CAPTCHA.INVALID_ITEM_HASH");
|
|
114
|
+
}
|
|
115
|
+
return solution;
|
|
116
|
+
}
|
|
117
|
+
const item = util.at(items, solution);
|
|
118
|
+
const hash = item.hash;
|
|
119
|
+
return hash;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function computeCaptchaSolutionHash(captcha) {
|
|
123
|
+
return common.hexHashArray([captcha.captchaId, captcha.captchaContentId, [...captcha.solution].sort(), captcha.salt]);
|
|
124
|
+
}
|
|
125
|
+
function computePendingRequestHash(captchaIds, userAccount, salt) {
|
|
126
|
+
return common.hexHashArray([...captchaIds.sort(), userAccount, salt]);
|
|
127
|
+
}
|
|
128
|
+
function parseCaptchaAssets(item, assetsResolver) {
|
|
129
|
+
return { ...item, path: (assetsResolver == null ? void 0 : assetsResolver.resolveAsset(item.data).getURL()) || item.data };
|
|
130
|
+
}
|
|
131
|
+
exports.NO_SOLUTION_VALUE = NO_SOLUTION_VALUE;
|
|
132
|
+
exports.captchaSort = captchaSort;
|
|
133
|
+
exports.compareCaptchaSolutions = compareCaptchaSolutions;
|
|
134
|
+
exports.computeCaptchaHash = computeCaptchaHash;
|
|
135
|
+
exports.computeCaptchaSolutionHash = computeCaptchaSolutionHash;
|
|
136
|
+
exports.computeItemHash = computeItemHash;
|
|
137
|
+
exports.computePendingRequestHash = computePendingRequestHash;
|
|
138
|
+
exports.getSolutionValueToHash = getSolutionValueToHash;
|
|
139
|
+
exports.matchItemsToSolutions = matchItemsToSolutions;
|
|
140
|
+
exports.parseAndSortCaptchaSolutions = parseAndSortCaptchaSolutions;
|
|
141
|
+
exports.parseCaptchaAssets = parseCaptchaAssets;
|
|
142
|
+
exports.parseCaptchaDataset = parseCaptchaDataset;
|
|
143
|
+
exports.sortAndComputeHashes = sortAndComputeHashes;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const merkle = require("./merkle.cjs");
|
|
4
|
+
const common = require("@prosopo/common");
|
|
5
|
+
const util = require("@prosopo/util");
|
|
6
|
+
const captcha = require("./captcha.cjs");
|
|
7
|
+
const logger = common.getLogger(`Info`, `dataset.ts`);
|
|
8
|
+
async function hashDatasetItems(datasetRaw) {
|
|
9
|
+
return datasetRaw.captchas.map(async (captcha$1) => {
|
|
10
|
+
const items = await Promise.all(captcha$1.items.map(async (item) => captcha.computeItemHash(item)));
|
|
11
|
+
return {
|
|
12
|
+
...captcha$1,
|
|
13
|
+
items
|
|
14
|
+
};
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
async function validateDatasetContent(datasetOriginal) {
|
|
18
|
+
const captchaPromises = await hashDatasetItems(datasetOriginal);
|
|
19
|
+
const captchas = await Promise.all(captchaPromises);
|
|
20
|
+
const dataset = {
|
|
21
|
+
...datasetOriginal,
|
|
22
|
+
captchas
|
|
23
|
+
};
|
|
24
|
+
const hashes = dataset.captchas.map((captcha2) => {
|
|
25
|
+
const captchaRaw = datasetOriginal.captchas.find((captchaRaw2) => "captchaId" in captchaRaw2 ? captchaRaw2.captchaId === captcha2.captchaId : false);
|
|
26
|
+
if (captchaRaw) {
|
|
27
|
+
return captcha2.items.every((item, index) => item.hash === util.at(captchaRaw.items, index).hash);
|
|
28
|
+
} else {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return hashes.every((hash) => hash);
|
|
33
|
+
}
|
|
34
|
+
async function buildDataset(datasetRaw) {
|
|
35
|
+
var _a, _b;
|
|
36
|
+
logger.info(`Adding solution hashes to dataset`);
|
|
37
|
+
const dataset = await addSolutionHashesToDataset(datasetRaw);
|
|
38
|
+
logger.info(`Building dataset merkle trees`);
|
|
39
|
+
const contentTree = await buildCaptchaTree(dataset, false, false, true);
|
|
40
|
+
const solutionTree = await buildCaptchaTree(dataset, true, true, false);
|
|
41
|
+
dataset.captchas = dataset.captchas.map((captcha2, index) => {
|
|
42
|
+
var _a2, _b2;
|
|
43
|
+
return {
|
|
44
|
+
...captcha2,
|
|
45
|
+
captchaId: util.at(solutionTree.leaves, index).hash,
|
|
46
|
+
captchaContentId: util.at(contentTree.leaves, index).hash,
|
|
47
|
+
datasetId: (_a2 = solutionTree.root) == null ? void 0 : _a2.hash,
|
|
48
|
+
datasetContentId: (_b2 = contentTree.root) == null ? void 0 : _b2.hash
|
|
49
|
+
};
|
|
50
|
+
});
|
|
51
|
+
dataset.solutionTree = solutionTree.layers;
|
|
52
|
+
dataset.contentTree = contentTree.layers;
|
|
53
|
+
dataset.datasetId = (_a = solutionTree.root) == null ? void 0 : _a.hash;
|
|
54
|
+
dataset.datasetContentId = (_b = contentTree.root) == null ? void 0 : _b.hash;
|
|
55
|
+
return dataset;
|
|
56
|
+
}
|
|
57
|
+
async function buildCaptchaTree(dataset, includeSolution, includeSalt, sortItemHashes) {
|
|
58
|
+
try {
|
|
59
|
+
const tree = new merkle.CaptchaMerkleTree();
|
|
60
|
+
const datasetWithItemHashes = { ...dataset };
|
|
61
|
+
const captchaHashes = datasetWithItemHashes.captchas.map((captcha$1) => captcha.computeCaptchaHash(captcha$1, includeSolution, includeSalt, sortItemHashes));
|
|
62
|
+
tree.build(captchaHashes);
|
|
63
|
+
return tree;
|
|
64
|
+
} catch (err) {
|
|
65
|
+
throw new common.ProsopoEnvError("DATASET.HASH_ERROR");
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function addSolutionHashesToDataset(datasetRaw) {
|
|
69
|
+
const captchaPromises = datasetRaw.captchas.map(async (captcha$1) => {
|
|
70
|
+
return {
|
|
71
|
+
...captcha$1,
|
|
72
|
+
items: captcha$1.items,
|
|
73
|
+
// some captcha challenges will not have a solution
|
|
74
|
+
...captcha$1.solution !== void 0 && { solution: captcha.matchItemsToSolutions(captcha$1.solution, captcha$1.items) }
|
|
75
|
+
};
|
|
76
|
+
});
|
|
77
|
+
const captchas = await Promise.all(captchaPromises);
|
|
78
|
+
return {
|
|
79
|
+
...datasetRaw,
|
|
80
|
+
captchas
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
exports.addSolutionHashesToDataset = addSolutionHashesToDataset;
|
|
84
|
+
exports.buildCaptchaTree = buildCaptchaTree;
|
|
85
|
+
exports.buildDataset = buildDataset;
|
|
86
|
+
exports.hashDatasetItems = hashDatasetItems;
|
|
87
|
+
exports.validateDatasetContent = validateDatasetContent;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const captcha = require("./captcha.cjs");
|
|
4
|
+
const merkle = require("./merkle.cjs");
|
|
5
|
+
const util = require("./util.cjs");
|
|
6
|
+
const dataset = require("./dataset.cjs");
|
|
7
|
+
exports.NO_SOLUTION_VALUE = captcha.NO_SOLUTION_VALUE;
|
|
8
|
+
exports.captchaSort = captcha.captchaSort;
|
|
9
|
+
exports.compareCaptchaSolutions = captcha.compareCaptchaSolutions;
|
|
10
|
+
exports.computeCaptchaHash = captcha.computeCaptchaHash;
|
|
11
|
+
exports.computeCaptchaSolutionHash = captcha.computeCaptchaSolutionHash;
|
|
12
|
+
exports.computeItemHash = captcha.computeItemHash;
|
|
13
|
+
exports.computePendingRequestHash = captcha.computePendingRequestHash;
|
|
14
|
+
exports.getSolutionValueToHash = captcha.getSolutionValueToHash;
|
|
15
|
+
exports.matchItemsToSolutions = captcha.matchItemsToSolutions;
|
|
16
|
+
exports.parseAndSortCaptchaSolutions = captcha.parseAndSortCaptchaSolutions;
|
|
17
|
+
exports.parseCaptchaAssets = captcha.parseCaptchaAssets;
|
|
18
|
+
exports.parseCaptchaDataset = captcha.parseCaptchaDataset;
|
|
19
|
+
exports.sortAndComputeHashes = captcha.sortAndComputeHashes;
|
|
20
|
+
exports.CaptchaMerkleTree = merkle.CaptchaMerkleTree;
|
|
21
|
+
exports.verifyProof = merkle.verifyProof;
|
|
22
|
+
exports.downloadImage = util.downloadImage;
|
|
23
|
+
exports.addSolutionHashesToDataset = dataset.addSolutionHashesToDataset;
|
|
24
|
+
exports.buildCaptchaTree = dataset.buildCaptchaTree;
|
|
25
|
+
exports.buildDataset = dataset.buildDataset;
|
|
26
|
+
exports.hashDatasetItems = dataset.hashDatasetItems;
|
|
27
|
+
exports.validateDatasetContent = dataset.validateDatasetContent;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const util = require("@prosopo/util");
|
|
4
|
+
const common = require("@prosopo/common");
|
|
5
|
+
class MerkleNode {
|
|
6
|
+
constructor(hash) {
|
|
7
|
+
this.hash = hash;
|
|
8
|
+
this.parent = null;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
class CaptchaMerkleTree {
|
|
12
|
+
constructor() {
|
|
13
|
+
this.leaves = [];
|
|
14
|
+
this.layers = [];
|
|
15
|
+
}
|
|
16
|
+
build(leaves) {
|
|
17
|
+
if (this.layers.length) {
|
|
18
|
+
this.layers = [];
|
|
19
|
+
}
|
|
20
|
+
const layerZero = [];
|
|
21
|
+
for (const leaf of leaves) {
|
|
22
|
+
const node = new MerkleNode(leaf);
|
|
23
|
+
this.leaves.push(node);
|
|
24
|
+
layerZero.push(node.hash);
|
|
25
|
+
}
|
|
26
|
+
this.layers.push(layerZero);
|
|
27
|
+
this.root = this.buildMerkleTree(this.leaves)[0];
|
|
28
|
+
}
|
|
29
|
+
buildMerkleTree(leaves) {
|
|
30
|
+
const numLeaves = leaves.length;
|
|
31
|
+
if (numLeaves === 1) {
|
|
32
|
+
return leaves;
|
|
33
|
+
}
|
|
34
|
+
const parents = [];
|
|
35
|
+
let leafIndex = 0;
|
|
36
|
+
const newLayer = [];
|
|
37
|
+
while (leafIndex < numLeaves) {
|
|
38
|
+
const leftChild = leaves[leafIndex];
|
|
39
|
+
if (leftChild === void 0) {
|
|
40
|
+
throw new Error("leftChild undefined");
|
|
41
|
+
}
|
|
42
|
+
const rightChild = leafIndex + 1 < numLeaves ? util.at(leaves, leafIndex + 1) : leftChild;
|
|
43
|
+
const parentNode = this.createParent(leftChild, rightChild);
|
|
44
|
+
newLayer.push(parentNode.hash);
|
|
45
|
+
parents.push(parentNode);
|
|
46
|
+
leafIndex += 2;
|
|
47
|
+
}
|
|
48
|
+
this.layers.push(newLayer);
|
|
49
|
+
return this.buildMerkleTree(parents);
|
|
50
|
+
}
|
|
51
|
+
createParent(leftChild, rightChild) {
|
|
52
|
+
const parent = new MerkleNode(common.hexHashArray([leftChild.hash, rightChild.hash]));
|
|
53
|
+
leftChild.parent = parent.hash;
|
|
54
|
+
rightChild.parent = parent.hash;
|
|
55
|
+
return parent;
|
|
56
|
+
}
|
|
57
|
+
proof(leafHash) {
|
|
58
|
+
const proofTree = [];
|
|
59
|
+
let layerNum = 0;
|
|
60
|
+
while (layerNum < this.layers.length - 1) {
|
|
61
|
+
const layer = this.layers[layerNum];
|
|
62
|
+
if (layer === void 0) {
|
|
63
|
+
throw new Error("layer undefined");
|
|
64
|
+
}
|
|
65
|
+
const leafIndex = layer.indexOf(leafHash);
|
|
66
|
+
let partnerIndex = leafIndex % 2 && leafIndex > 0 ? leafIndex - 1 : leafIndex + 1;
|
|
67
|
+
if (partnerIndex > layer.length - 1) {
|
|
68
|
+
partnerIndex = leafIndex;
|
|
69
|
+
}
|
|
70
|
+
const pair = [leafHash];
|
|
71
|
+
const partner = util.at(layer, partnerIndex);
|
|
72
|
+
if (partnerIndex > leafIndex) {
|
|
73
|
+
pair.push(partner);
|
|
74
|
+
} else {
|
|
75
|
+
pair.unshift(partner);
|
|
76
|
+
}
|
|
77
|
+
proofTree.push([util.at(pair, 0), util.at(pair, 1)]);
|
|
78
|
+
layerNum += 1;
|
|
79
|
+
leafHash = common.hexHashArray(pair);
|
|
80
|
+
}
|
|
81
|
+
const last = util.at(this.layers, this.layers.length - 1);
|
|
82
|
+
return [...proofTree, [util.at(last, 0)]];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function verifyProof(leaf, proof) {
|
|
86
|
+
try {
|
|
87
|
+
if (util.at(proof, 0).indexOf(leaf) === -1) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
for (const [layerIndex, layer] of proof.entries()) {
|
|
91
|
+
leaf = common.hexHashArray(layer);
|
|
92
|
+
if (util.at(proof, layerIndex + 1).indexOf(leaf) === -1) {
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
const last = util.at(proof, proof.length - 1);
|
|
96
|
+
if (leaf === util.at(last, 0)) {
|
|
97
|
+
return true;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
} catch (err) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
exports.CaptchaMerkleTree = CaptchaMerkleTree;
|
|
106
|
+
exports.verifyProof = verifyProof;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const common = require("@prosopo/common");
|
|
4
|
+
async function downloadImage(url) {
|
|
5
|
+
try {
|
|
6
|
+
const response = await fetch(url);
|
|
7
|
+
if (!response.ok) {
|
|
8
|
+
throw new Error(`Network response was not ok, status: ${response.status}`);
|
|
9
|
+
}
|
|
10
|
+
const buffer = await response.arrayBuffer();
|
|
11
|
+
return new Uint8Array(buffer);
|
|
12
|
+
} catch (error) {
|
|
13
|
+
throw new common.ProsopoEnvError(error);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.downloadImage = downloadImage;
|
|
@@ -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,EAGH,4BAA4B,EAE5B,YAAY,EACf,MAAM,gBAAgB,CAAA;
|
|
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,EAE5B,YAAY,EACf,MAAM,gBAAgB,CAAA;AA0BvB,eAAO,MAAM,YAAY,QAAO,QAAQ,eAAe,CAatD,CAAA;AAgBD,eAAO,MAAM,UAAU,WAAY,4BAA4B;;;;;;;;CAQ9D,CAAA;AAKD,wBAAgB,OAAO,CACnB,cAAc,EAAE,wBAAwB,EACxC,KAAK,EAAE,eAAe,EACtB,aAAa,EAAE,uBAAuB,EACtC,SAAS,EAAE,mBAAmB;;;;mBA+TR,MAAM;;yBAkLM,YAAY;EAiBjD"}
|