@prosopo/provider 2.0.0 → 2.0.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/captcha.d.ts.map +1 -1
- package/dist/api/captcha.js +15 -9
- package/dist/api/captcha.js.map +1 -1
- package/dist/api/captchaScheduler.d.ts +1 -1
- package/dist/api/captchaScheduler.d.ts.map +1 -1
- package/dist/api/captchaScheduler.js +10 -4
- package/dist/api/captchaScheduler.js.map +1 -1
- package/dist/api/verify.d.ts.map +1 -1
- package/dist/api/verify.js +22 -9
- package/dist/api/verify.js.map +1 -1
- package/dist/cjs/api/captcha.cjs +51 -39
- package/dist/cjs/api/captchaScheduler.cjs +17 -4
- package/dist/cjs/api/verify.cjs +22 -10
- package/dist/cjs/index.cjs +0 -2
- package/dist/cjs/tasks/dataset/datasetTasks.cjs +69 -10
- package/dist/cjs/tasks/imgCaptcha/imgCaptchaTasks.cjs +35 -34
- package/dist/cjs/tasks/powCaptcha/powTasks.cjs +53 -28
- package/dist/cjs/tasks/powCaptcha/powTasksUtils.cjs +0 -29
- package/dist/cjs/util.cjs +1 -24
- package/dist/tasks/dataset/datasetTasks.d.ts +1 -1
- package/dist/tasks/dataset/datasetTasks.d.ts.map +1 -1
- package/dist/tasks/dataset/datasetTasks.js +42 -7
- package/dist/tasks/dataset/datasetTasks.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts +5 -5
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js +22 -27
- package/dist/tasks/imgCaptcha/imgCaptchaTasks.js.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.d.ts.map +1 -1
- package/dist/tasks/imgCaptcha/imgCaptchaTasksUtils.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.d.ts +2 -2
- package/dist/tasks/powCaptcha/powTasks.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasks.js +40 -23
- package/dist/tasks/powCaptcha/powTasks.js.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.d.ts +0 -2
- package/dist/tasks/powCaptcha/powTasksUtils.d.ts.map +1 -1
- package/dist/tasks/powCaptcha/powTasksUtils.js +0 -27
- package/dist/tasks/powCaptcha/powTasksUtils.js.map +1 -1
- package/dist/tests/integration/imgCaptcha.test.js +8 -4
- package/dist/tests/integration/imgCaptcha.test.js.map +1 -1
- package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts +5 -1
- package/dist/tests/integration/mocks/solvedTestCaptchas.d.ts.map +1 -1
- package/dist/tests/integration/mocks/solvedTestCaptchas.js +4 -0
- package/dist/tests/integration/mocks/solvedTestCaptchas.js.map +1 -1
- package/dist/tests/integration/powCaptcha.test.js +11 -9
- package/dist/tests/integration/powCaptcha.test.js.map +1 -1
- package/dist/tests/unit/api/captchaScheduler.test.js +19 -5
- package/dist/tests/unit/api/captchaScheduler.test.js.map +1 -1
- package/dist/tests/unit/tasks/dataset/datasetTasks.test.js +51 -4
- package/dist/tests/unit/tasks/dataset/datasetTasks.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.test.js +26 -24
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasks.test.js.map +1 -1
- package/dist/tests/unit/tasks/imgCaptcha/imgCaptchaTasksUtils.test.js.map +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasks.test.js +103 -32
- package/dist/tests/unit/tasks/powCaptcha/powTasks.test.js.map +1 -1
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.test.js +5 -34
- package/dist/tests/unit/tasks/powCaptcha/powTasksUtils.test.js.map +1 -1
- package/dist/util.d.ts +0 -7
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +1 -21
- package/dist/util.js.map +1 -1
- package/package.json +72 -74
|
@@ -29,7 +29,7 @@ class ImgCaptchaManager {
|
|
|
29
29
|
}
|
|
30
30
|
return captchaDocs;
|
|
31
31
|
}
|
|
32
|
-
async getRandomCaptchasAndRequestHash(datasetId, userAccount) {
|
|
32
|
+
async getRandomCaptchasAndRequestHash(datasetId, userAccount, ipAddress) {
|
|
33
33
|
const dataset = await this.db.getDatasetDetails(datasetId);
|
|
34
34
|
if (!dataset) {
|
|
35
35
|
throw new common.ProsopoEnvError("DATABASE.DATASET_GET_FAILED", {
|
|
@@ -66,8 +66,8 @@ class ImgCaptchaManager {
|
|
|
66
66
|
salt
|
|
67
67
|
);
|
|
68
68
|
const currentTime = Date.now();
|
|
69
|
-
const
|
|
70
|
-
this.pair.sign(util$1.stringToHex(
|
|
69
|
+
const signedRequestHash = util$1.u8aToHex(
|
|
70
|
+
this.pair.sign(util$1.stringToHex(requestHash))
|
|
71
71
|
);
|
|
72
72
|
const timeLimit = captchas.map((captcha) => captcha.timeLimitMs || types.DEFAULT_IMAGE_CAPTCHA_TIMEOUT).reduce((a, b) => a + b, 0);
|
|
73
73
|
const deadlineTs = timeLimit + currentTime;
|
|
@@ -77,13 +77,14 @@ class ImgCaptchaManager {
|
|
|
77
77
|
requestHash,
|
|
78
78
|
salt,
|
|
79
79
|
deadlineTs,
|
|
80
|
-
|
|
80
|
+
currentTime,
|
|
81
|
+
ipAddress
|
|
81
82
|
);
|
|
82
83
|
return {
|
|
83
84
|
captchas,
|
|
84
85
|
requestHash,
|
|
85
86
|
timestamp: currentTime,
|
|
86
|
-
|
|
87
|
+
signedRequestHash
|
|
87
88
|
};
|
|
88
89
|
}
|
|
89
90
|
/**
|
|
@@ -92,35 +93,36 @@ class ImgCaptchaManager {
|
|
|
92
93
|
* @param {string} dappAccount
|
|
93
94
|
* @param {string} requestHash
|
|
94
95
|
* @param {JSON} captchas
|
|
95
|
-
* @param {string}
|
|
96
|
+
* @param {string} userRequestHashSignature
|
|
96
97
|
* @param timestamp
|
|
97
|
-
* @param
|
|
98
|
+
* @param providerRequestHashSignature
|
|
99
|
+
* @param ipAddress
|
|
98
100
|
* @return {Promise<DappUserSolutionResult>} result containing the contract event
|
|
99
101
|
*/
|
|
100
|
-
async dappUserSolution(userAccount, dappAccount, requestHash, captchas,
|
|
102
|
+
async dappUserSolution(userAccount, dappAccount, requestHash, captchas, userRequestHashSignature, timestamp, providerRequestHashSignature, ipAddress) {
|
|
101
103
|
const verification = utilCrypto.signatureVerify(
|
|
102
104
|
util$1.stringToHex(requestHash),
|
|
103
|
-
|
|
105
|
+
userRequestHashSignature,
|
|
104
106
|
userAccount
|
|
105
107
|
);
|
|
106
108
|
if (!verification.isValid) {
|
|
107
|
-
this.logger.info("Invalid requestHash signature");
|
|
109
|
+
this.logger.info("Invalid user requestHash signature");
|
|
108
110
|
throw new common.ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
|
|
109
111
|
context: { failedFuncName: this.dappUserSolution.name, userAccount }
|
|
110
112
|
});
|
|
111
113
|
}
|
|
112
|
-
const
|
|
113
|
-
util$1.stringToHex(
|
|
114
|
-
|
|
114
|
+
const providerRequestHashSignatureVerify = utilCrypto.signatureVerify(
|
|
115
|
+
util$1.stringToHex(requestHash.toString()),
|
|
116
|
+
providerRequestHashSignature,
|
|
115
117
|
this.pair.address
|
|
116
118
|
);
|
|
117
|
-
if (!
|
|
118
|
-
this.logger.info("Invalid
|
|
119
|
+
if (!providerRequestHashSignatureVerify.isValid) {
|
|
120
|
+
this.logger.info("Invalid provider requestHash signature");
|
|
119
121
|
throw new common.ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
|
|
120
122
|
context: {
|
|
121
123
|
failedFuncName: this.dappUserSolution.name,
|
|
122
124
|
userAccount,
|
|
123
|
-
error: "
|
|
125
|
+
error: "requestHash signature is invalid"
|
|
124
126
|
}
|
|
125
127
|
});
|
|
126
128
|
}
|
|
@@ -136,7 +138,6 @@ class ImgCaptchaManager {
|
|
|
136
138
|
userAccount,
|
|
137
139
|
unverifiedCaptchaIds
|
|
138
140
|
);
|
|
139
|
-
console.log("Pending request", pendingRequest);
|
|
140
141
|
if (pendingRequest) {
|
|
141
142
|
const { storedCaptchas, receivedCaptchas, captchaIds } = await this.validateReceivedCaptchasAgainstStoredCaptchas(captchas);
|
|
142
143
|
const { tree, commitmentId } = imgCaptchaTasksUtils.buildTreeAndGetCommitmentId(receivedCaptchas);
|
|
@@ -146,28 +147,21 @@ class ImgCaptchaManager {
|
|
|
146
147
|
context: { failedFuncName: this.dappUserSolution.name }
|
|
147
148
|
});
|
|
148
149
|
}
|
|
149
|
-
const userSignature = util$1.hexToU8a(requestHashSignature);
|
|
150
150
|
await this.db.updateDappUserPendingStatus(requestHash);
|
|
151
151
|
const commit = {
|
|
152
152
|
id: commitmentId,
|
|
153
153
|
userAccount,
|
|
154
|
-
|
|
154
|
+
dappAccount,
|
|
155
155
|
providerAccount: this.pair.address,
|
|
156
156
|
datasetId,
|
|
157
|
-
status: types.CaptchaStatus.pending,
|
|
158
|
-
userSignature:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
processed: false,
|
|
164
|
-
batched: false,
|
|
165
|
-
stored: false,
|
|
166
|
-
requestedAtTimestamp: timestamp
|
|
157
|
+
result: { status: types.CaptchaStatus.pending },
|
|
158
|
+
userSignature: userRequestHashSignature,
|
|
159
|
+
userSubmitted: true,
|
|
160
|
+
serverChecked: false,
|
|
161
|
+
requestedAtTimestamp: timestamp,
|
|
162
|
+
ipAddress
|
|
167
163
|
};
|
|
168
164
|
await this.db.storeDappUserSolution(receivedCaptchas, commit);
|
|
169
|
-
console.log(receivedCaptchas);
|
|
170
|
-
console.log(storedCaptchas);
|
|
171
165
|
if (datasets.compareCaptchaSolutions(receivedCaptchas, storedCaptchas)) {
|
|
172
166
|
response = {
|
|
173
167
|
captchas: captchaIds.map((id) => ({
|
|
@@ -178,6 +172,10 @@ class ImgCaptchaManager {
|
|
|
178
172
|
};
|
|
179
173
|
await this.db.approveDappUserCommitment(commitmentId);
|
|
180
174
|
} else {
|
|
175
|
+
await this.db.disapproveDappUserCommitment(
|
|
176
|
+
commitmentId,
|
|
177
|
+
"CAPTCHA.INVALID_SOLUTION"
|
|
178
|
+
);
|
|
181
179
|
response = {
|
|
182
180
|
captchas: captchaIds.map((id) => ({
|
|
183
181
|
captchaId: id,
|
|
@@ -262,11 +260,14 @@ class ImgCaptchaManager {
|
|
|
262
260
|
return dappUserSolution;
|
|
263
261
|
}
|
|
264
262
|
/* Check if dapp user has verified solution in cache */
|
|
265
|
-
async getDappUserCommitmentByAccount(userAccount) {
|
|
266
|
-
const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(
|
|
263
|
+
async getDappUserCommitmentByAccount(userAccount, dappAccount) {
|
|
264
|
+
const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(
|
|
265
|
+
userAccount,
|
|
266
|
+
dappAccount
|
|
267
|
+
);
|
|
267
268
|
if (dappUserSolutions.length > 0) {
|
|
268
269
|
for (const dappUserSolution of dappUserSolutions) {
|
|
269
|
-
if (dappUserSolution.status === types.CaptchaStatus.approved) {
|
|
270
|
+
if (dappUserSolution.result.status === types.CaptchaStatus.approved) {
|
|
270
271
|
return dappUserSolution;
|
|
271
272
|
}
|
|
272
273
|
}
|
|
@@ -5,6 +5,7 @@ const common = require("@prosopo/common");
|
|
|
5
5
|
const types = require("@prosopo/types");
|
|
6
6
|
const util$1 = require("@prosopo/util");
|
|
7
7
|
const powTasksUtils = require("./powTasksUtils.cjs");
|
|
8
|
+
const logger = common.getLoggerDefault();
|
|
8
9
|
class PowCaptchaManager {
|
|
9
10
|
constructor(pair, db) {
|
|
10
11
|
this.pair = pair;
|
|
@@ -20,18 +21,14 @@ class PowCaptchaManager {
|
|
|
20
21
|
*/
|
|
21
22
|
async getPowCaptchaChallenge(userAccount, dappAccount, origin) {
|
|
22
23
|
const difficulty = 4;
|
|
23
|
-
const
|
|
24
|
-
const challenge = `${
|
|
24
|
+
const requestedAtTimestamp = Date.now();
|
|
25
|
+
const challenge = `${requestedAtTimestamp}___${userAccount}___${dappAccount}`;
|
|
25
26
|
const challengeSignature = util.u8aToHex(this.pair.sign(util.stringToHex(challenge)));
|
|
26
|
-
const timestampSignature = util.u8aToHex(
|
|
27
|
-
this.pair.sign(util.stringToHex(timestamp.toString()))
|
|
28
|
-
);
|
|
29
27
|
return {
|
|
30
28
|
challenge,
|
|
31
29
|
difficulty,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
timestampSignature
|
|
30
|
+
providerSignature: challengeSignature,
|
|
31
|
+
requestedAtTimestamp
|
|
35
32
|
};
|
|
36
33
|
}
|
|
37
34
|
/**
|
|
@@ -39,36 +36,62 @@ class PowCaptchaManager {
|
|
|
39
36
|
*
|
|
40
37
|
* @param {string} challenge - the starting string for the PoW challenge
|
|
41
38
|
* @param {string} difficulty - how many leading zeroes the solution must have
|
|
42
|
-
* @param {string}
|
|
39
|
+
* @param {string} providerChallengeSignature - proof that the Provider provided the challenge
|
|
43
40
|
* @param {string} nonce - the string that the user has found that satisfies the PoW challenge
|
|
44
41
|
* @param {number} timeout - the time in milliseconds since the Provider was selected to provide the PoW captcha
|
|
45
|
-
* @param
|
|
42
|
+
* @param {string} userTimestampSignature
|
|
43
|
+
* @param ipAddress
|
|
46
44
|
*/
|
|
47
|
-
async verifyPowCaptchaSolution(challenge, difficulty,
|
|
48
|
-
powTasksUtils.
|
|
45
|
+
async verifyPowCaptchaSolution(challenge, difficulty, providerChallengeSignature, nonce, timeout, userTimestampSignature, ipAddress) {
|
|
46
|
+
powTasksUtils.checkPowSignature(
|
|
47
|
+
challenge,
|
|
48
|
+
providerChallengeSignature,
|
|
49
|
+
this.pair.address,
|
|
50
|
+
types.ApiParams.challenge
|
|
51
|
+
);
|
|
49
52
|
const challengeSplit = challenge.split(this.POW_SEPARATOR);
|
|
50
53
|
const timestamp = parseInt(util$1.at(challengeSplit, 0));
|
|
51
54
|
const userAccount = util$1.at(challengeSplit, 1);
|
|
52
|
-
const dappAccount = util$1.at(challengeSplit, 2);
|
|
53
55
|
powTasksUtils.checkPowSignature(
|
|
54
56
|
timestamp.toString(),
|
|
55
|
-
|
|
57
|
+
userTimestampSignature,
|
|
56
58
|
userAccount,
|
|
57
59
|
types.ApiParams.timestamp
|
|
58
60
|
);
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
const challengeRecord = await this.db.getPowCaptchaRecordByChallenge(challenge);
|
|
62
|
+
if (!challengeRecord) {
|
|
63
|
+
logger.debug("No record of this challenge");
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
if (!util$1.verifyRecency(challenge, timeout)) {
|
|
67
|
+
await this.db.updatePowCaptchaRecord(
|
|
68
|
+
challenge,
|
|
69
|
+
{
|
|
70
|
+
status: types.CaptchaStatus.disapproved,
|
|
71
|
+
reason: "CAPTCHA.INVALID_TIMESTAMP"
|
|
72
|
+
},
|
|
73
|
+
false,
|
|
74
|
+
true,
|
|
75
|
+
userTimestampSignature
|
|
76
|
+
);
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
const correct = powTasksUtils.validateSolution(nonce, challenge, difficulty);
|
|
80
|
+
let result = { status: types.CaptchaStatus.approved };
|
|
81
|
+
if (!correct) {
|
|
82
|
+
result = {
|
|
83
|
+
status: types.CaptchaStatus.disapproved,
|
|
84
|
+
reason: "CAPTCHA.INVALID_SOLUTION"
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
await this.db.updatePowCaptchaRecord(
|
|
67
88
|
challenge,
|
|
68
|
-
|
|
69
|
-
false
|
|
89
|
+
result,
|
|
90
|
+
false,
|
|
91
|
+
true,
|
|
92
|
+
userTimestampSignature
|
|
70
93
|
);
|
|
71
|
-
return
|
|
94
|
+
return correct;
|
|
72
95
|
}
|
|
73
96
|
/**
|
|
74
97
|
* @description Verifies a PoW Captcha for a given user and dapp. This is called by the server to verify the user's solution
|
|
@@ -88,7 +111,7 @@ class PowCaptchaManager {
|
|
|
88
111
|
}
|
|
89
112
|
});
|
|
90
113
|
}
|
|
91
|
-
if (challengeRecord.
|
|
114
|
+
if (challengeRecord.serverChecked) return false;
|
|
92
115
|
const challengeDappAccount = challengeRecord.dappAccount;
|
|
93
116
|
if (dappAccount !== challengeDappAccount) {
|
|
94
117
|
throw new common.ProsopoEnvError("CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND", {
|
|
@@ -99,8 +122,10 @@ class PowCaptchaManager {
|
|
|
99
122
|
}
|
|
100
123
|
});
|
|
101
124
|
}
|
|
102
|
-
|
|
103
|
-
await this.db.
|
|
125
|
+
util$1.verifyRecency(challenge, timeout);
|
|
126
|
+
await this.db.markDappUserPoWCommitmentsChecked([
|
|
127
|
+
challengeRecord.challenge
|
|
128
|
+
]);
|
|
104
129
|
return true;
|
|
105
130
|
}
|
|
106
131
|
}
|
|
@@ -4,22 +4,7 @@ const sha256 = require("@noble/hashes/sha256");
|
|
|
4
4
|
const util = require("@polkadot/util");
|
|
5
5
|
const utilCrypto = require("@polkadot/util-crypto");
|
|
6
6
|
const common = require("@prosopo/common");
|
|
7
|
-
const contract = require("@prosopo/contract");
|
|
8
7
|
const validateSolution = (nonce, challenge, difficulty) => Array.from(sha256.sha256(new TextEncoder().encode(nonce + challenge))).map((byte) => byte.toString(16).padStart(2, "0")).join("").startsWith("0".repeat(difficulty));
|
|
9
|
-
const checkPowSolution = (nonce, challenge, difficulty) => {
|
|
10
|
-
const solutionValid = validateSolution(nonce, challenge, difficulty);
|
|
11
|
-
if (!solutionValid) {
|
|
12
|
-
throw new common.ProsopoContractError("API.CAPTCHA_FAILED", {
|
|
13
|
-
context: {
|
|
14
|
-
ERROR: "Captcha solution is invalid",
|
|
15
|
-
failedFuncName: checkPowSolution.name,
|
|
16
|
-
nonce,
|
|
17
|
-
challenge,
|
|
18
|
-
difficulty
|
|
19
|
-
}
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
8
|
const checkPowSignature = (challenge, signature, address, signatureType) => {
|
|
24
9
|
const signatureVerification = utilCrypto.signatureVerify(
|
|
25
10
|
util.stringToHex(challenge),
|
|
@@ -37,19 +22,5 @@ const checkPowSignature = (challenge, signature, address, signatureType) => {
|
|
|
37
22
|
});
|
|
38
23
|
}
|
|
39
24
|
};
|
|
40
|
-
const checkRecentPowSolution = (challenge, timeout) => {
|
|
41
|
-
const recent = contract.verifyRecency(challenge, timeout);
|
|
42
|
-
if (!recent) {
|
|
43
|
-
throw new common.ProsopoContractError("CONTRACT.INVALID_BLOCKHASH", {
|
|
44
|
-
context: {
|
|
45
|
-
ERROR: `Block in which the Provider was selected must be within the last ${timeout / 1e3} seconds`,
|
|
46
|
-
failedFuncName: checkRecentPowSolution.name,
|
|
47
|
-
challenge
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
};
|
|
52
25
|
exports.checkPowSignature = checkPowSignature;
|
|
53
|
-
exports.checkPowSolution = checkPowSolution;
|
|
54
|
-
exports.checkRecentPowSolution = checkRecentPowSolution;
|
|
55
26
|
exports.validateSolution = validateSolution;
|
package/dist/cjs/util.cjs
CHANGED
|
@@ -26,27 +26,6 @@ function shuffleArray(array) {
|
|
|
26
26
|
}
|
|
27
27
|
return array;
|
|
28
28
|
}
|
|
29
|
-
async function promiseQueue(array) {
|
|
30
|
-
const ret = [];
|
|
31
|
-
await [...array, () => Promise.resolve(void 0)].reduce(
|
|
32
|
-
(promise, curr, i) => {
|
|
33
|
-
return promise.then((res) => {
|
|
34
|
-
if (res) {
|
|
35
|
-
ret.push({ data: res });
|
|
36
|
-
}
|
|
37
|
-
return curr();
|
|
38
|
-
}).catch((err) => {
|
|
39
|
-
ret.push({ data: err });
|
|
40
|
-
return curr();
|
|
41
|
-
});
|
|
42
|
-
},
|
|
43
|
-
Promise.resolve(void 0)
|
|
44
|
-
);
|
|
45
|
-
return ret;
|
|
46
|
-
}
|
|
47
|
-
function parseBlockNumber(blockNumberString) {
|
|
48
|
-
return Number.parseInt(blockNumberString.replace(/,/g, ""));
|
|
49
|
-
}
|
|
50
29
|
async function checkIfTaskIsRunning(taskName, db) {
|
|
51
30
|
const runningTask = await db.getLastScheduledTaskStatus(
|
|
52
31
|
taskName,
|
|
@@ -54,7 +33,7 @@ async function checkIfTaskIsRunning(taskName, db) {
|
|
|
54
33
|
);
|
|
55
34
|
if (runningTask) {
|
|
56
35
|
const completedTask = await db.getScheduledTaskStatus(
|
|
57
|
-
runningTask.
|
|
36
|
+
runningTask.id,
|
|
58
37
|
types.ScheduledTaskStatus.Completed
|
|
59
38
|
);
|
|
60
39
|
return !completedTask;
|
|
@@ -63,6 +42,4 @@ async function checkIfTaskIsRunning(taskName, db) {
|
|
|
63
42
|
}
|
|
64
43
|
exports.checkIfTaskIsRunning = checkIfTaskIsRunning;
|
|
65
44
|
exports.encodeStringAddress = encodeStringAddress;
|
|
66
|
-
exports.parseBlockNumber = parseBlockNumber;
|
|
67
|
-
exports.promiseQueue = promiseQueue;
|
|
68
45
|
exports.shuffleArray = shuffleArray;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Logger } from "@prosopo/common";
|
|
2
|
-
import
|
|
2
|
+
import { CaptchaConfig, DatasetRaw, ProsopoConfigOutput, StoredEvents } from "@prosopo/types";
|
|
3
3
|
import type { Database } from "@prosopo/types-database";
|
|
4
4
|
export declare class DatasetManager {
|
|
5
5
|
config: ProsopoConfigOutput;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datasetTasks.d.ts","sourceRoot":"","sources":["../../../src/tasks/dataset/datasetTasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAgB9C,OAAO,
|
|
1
|
+
{"version":3,"file":"datasetTasks.d.ts","sourceRoot":"","sources":["../../../src/tasks/dataset/datasetTasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAgB9C,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EAGnB,YAAY,EACb,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGxD,qBAAa,cAAc;IACzB,MAAM,EAAE,mBAAmB,CAAC;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,aAAa,CAAC;IAC7B,EAAE,EAAE,QAAQ,CAAC;gBAGX,MAAM,EAAE,mBAAmB,EAC3B,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa,EAC5B,EAAE,EAAE,QAAQ;IAaR,0BAA0B,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;IAKrD,kBAAkB,CAAC,UAAU,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBzD,gBAAgB,CAAC,MAAM,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM;IAYxD,wBAAwB,IAAI,OAAO,CAAC,IAAI,CAAC;CA+FhD"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { saveCaptchaEvent, saveCaptchas } from "@prosopo/database";
|
|
2
2
|
import { parseCaptchaDataset } from "@prosopo/datasets";
|
|
3
|
+
import { ScheduledTaskNames, ScheduledTaskStatus, } from "@prosopo/types";
|
|
3
4
|
import { providerValidateDataset } from "./datasetTasksUtils.js";
|
|
4
5
|
export class DatasetManager {
|
|
5
6
|
constructor(config, logger, captchaConfig, db) {
|
|
@@ -28,13 +29,47 @@ export class DatasetManager {
|
|
|
28
29
|
this.logger.info("Mongo env not set");
|
|
29
30
|
return;
|
|
30
31
|
}
|
|
31
|
-
const
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
32
|
+
const lastTask = await this.db.getLastScheduledTaskStatus(ScheduledTaskNames.StoreCommitmentsExternal, ScheduledTaskStatus.Completed);
|
|
33
|
+
const taskID = await this.db.createScheduledTaskStatus(ScheduledTaskNames.StoreCommitmentsExternal, ScheduledTaskStatus.Running);
|
|
34
|
+
try {
|
|
35
|
+
let commitments = await this.db.getUnstoredDappUserCommitments();
|
|
36
|
+
let powRecords = await this.db.getUnstoredDappUserPoWCommitments();
|
|
37
|
+
if (lastTask) {
|
|
38
|
+
this.logger.info(`Filtering records to only get updated records: ${JSON.stringify(lastTask)}`);
|
|
39
|
+
this.logger.info("Last task ran at ", new Date(lastTask.updated || 0));
|
|
40
|
+
commitments = commitments.filter((commitment) => lastTask.updated &&
|
|
41
|
+
commitment.lastUpdatedTimestamp &&
|
|
42
|
+
(commitment.lastUpdatedTimestamp > lastTask.updated ||
|
|
43
|
+
!commitment.lastUpdatedTimestamp));
|
|
44
|
+
this.logger.info("PoW Records to store: ", powRecords.map((pr) => ({
|
|
45
|
+
challenge: pr.challenge,
|
|
46
|
+
lastUpdatedTimestamp: new Date(pr.lastUpdatedTimestamp || 0),
|
|
47
|
+
})));
|
|
48
|
+
powRecords = powRecords.filter((commitment) => {
|
|
49
|
+
return (lastTask.updated &&
|
|
50
|
+
commitment.lastUpdatedTimestamp &&
|
|
51
|
+
(commitment.lastUpdatedTimestamp > lastTask.updated ||
|
|
52
|
+
!commitment.lastUpdatedTimestamp));
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
if (commitments.length || powRecords.length) {
|
|
56
|
+
this.logger.info(`Storing ${commitments.length} commitments externally`);
|
|
57
|
+
this.logger.info(`Storing ${powRecords.length} pow challenges externally`);
|
|
58
|
+
await saveCaptchas(commitments, powRecords, this.config.mongoCaptchaUri);
|
|
59
|
+
await this.db.markDappUserCommitmentsStored(commitments.map((commitment) => commitment.id));
|
|
60
|
+
await this.db.markDappUserPoWCommitmentsStored(powRecords.map((powRecords) => powRecords.challenge));
|
|
61
|
+
await this.db.updateScheduledTaskStatus(taskID, ScheduledTaskStatus.Completed, {
|
|
62
|
+
data: {
|
|
63
|
+
commitments: commitments.map((c) => c.id),
|
|
64
|
+
powRecords: powRecords.map((pr) => pr.challenge),
|
|
65
|
+
},
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
this.logger.error(e);
|
|
71
|
+
await this.db.updateScheduledTaskStatus(taskID, ScheduledTaskStatus.Failed, { error: e.toString() });
|
|
72
|
+
}
|
|
38
73
|
}
|
|
39
74
|
}
|
|
40
75
|
//# sourceMappingURL=datasetTasks.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"datasetTasks.js","sourceRoot":"","sources":["../../../src/tasks/dataset/datasetTasks.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAcnE,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"datasetTasks.js","sourceRoot":"","sources":["../../../src/tasks/dataset/datasetTasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { KeyringPair } from "@polkadot/keyring/types";
|
|
2
2
|
import { type Logger } from "@prosopo/common";
|
|
3
3
|
import { type Captcha, type CaptchaConfig, type CaptchaSolution, type DappUserSolutionResult, type Hash, type PendingCaptchaRequest } from "@prosopo/types";
|
|
4
|
-
import
|
|
4
|
+
import { Database, UserCommitmentRecord } from "@prosopo/types-database";
|
|
5
5
|
export declare class ImgCaptchaManager {
|
|
6
6
|
db: Database;
|
|
7
7
|
pair: KeyringPair;
|
|
@@ -9,13 +9,13 @@ export declare class ImgCaptchaManager {
|
|
|
9
9
|
captchaConfig: CaptchaConfig;
|
|
10
10
|
constructor(db: Database, pair: KeyringPair, logger: Logger, captchaConfig: CaptchaConfig);
|
|
11
11
|
getCaptchaWithProof(datasetId: Hash, solved: boolean, size: number): Promise<Captcha[]>;
|
|
12
|
-
getRandomCaptchasAndRequestHash(datasetId: string, userAccount: string): Promise<{
|
|
12
|
+
getRandomCaptchasAndRequestHash(datasetId: string, userAccount: string, ipAddress: string): Promise<{
|
|
13
13
|
captchas: Captcha[];
|
|
14
14
|
requestHash: string;
|
|
15
15
|
timestamp: number;
|
|
16
|
-
|
|
16
|
+
signedRequestHash: string;
|
|
17
17
|
}>;
|
|
18
|
-
dappUserSolution(userAccount: string, dappAccount: string, requestHash: string, captchas: CaptchaSolution[],
|
|
18
|
+
dappUserSolution(userAccount: string, dappAccount: string, requestHash: string, captchas: CaptchaSolution[], userRequestHashSignature: string, timestamp: number, providerRequestHashSignature: string, ipAddress: string): Promise<DappUserSolutionResult>;
|
|
19
19
|
validateReceivedCaptchasAgainstStoredCaptchas(captchas: CaptchaSolution[]): Promise<{
|
|
20
20
|
storedCaptchas: Captcha[];
|
|
21
21
|
receivedCaptchas: CaptchaSolution[];
|
|
@@ -23,6 +23,6 @@ export declare class ImgCaptchaManager {
|
|
|
23
23
|
}>;
|
|
24
24
|
validateDappUserSolutionRequestIsPending(requestHash: string, pendingRecord: PendingCaptchaRequest, userAccount: string, captchaIds: string[]): Promise<boolean>;
|
|
25
25
|
getDappUserCommitmentById(commitmentId: string): Promise<UserCommitmentRecord>;
|
|
26
|
-
getDappUserCommitmentByAccount(userAccount: string): Promise<UserCommitmentRecord | undefined>;
|
|
26
|
+
getDappUserCommitmentByAccount(userAccount: string, dappAccount: string): Promise<UserCommitmentRecord | undefined>;
|
|
27
27
|
}
|
|
28
28
|
//# sourceMappingURL=imgCaptchaTasks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imgCaptchaTasks.d.ts","sourceRoot":"","sources":["../../../src/tasks/imgCaptcha/imgCaptchaTasks.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,KAAK,MAAM,EAAmB,MAAM,iBAAiB,CAAC;AAM/D,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,eAAe,
|
|
1
|
+
{"version":3,"file":"imgCaptchaTasks.d.ts","sourceRoot":"","sources":["../../../src/tasks/imgCaptcha/imgCaptchaTasks.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAG3D,OAAO,EAAE,KAAK,MAAM,EAAmB,MAAM,iBAAiB,CAAC;AAM/D,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,aAAa,EAClB,KAAK,eAAe,EAEpB,KAAK,sBAAsB,EAE3B,KAAK,IAAI,EACT,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,QAAQ,EAER,oBAAoB,EACrB,MAAM,yBAAyB,CAAC;AAKjC,qBAAa,iBAAiB;IAC5B,EAAE,EAAE,QAAQ,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,aAAa,CAAC;gBAG3B,EAAE,EAAE,QAAQ,EACZ,IAAI,EAAE,WAAW,EACjB,MAAM,EAAE,MAAM,EACd,aAAa,EAAE,aAAa;IAQxB,mBAAmB,CACvB,SAAS,EAAE,IAAI,EACf,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,OAAO,EAAE,CAAC;IAgBf,+BAA+B,CACnC,SAAS,EAAE,MAAM,EACjB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;QACT,QAAQ,EAAE,OAAO,EAAE,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;QACpB,SAAS,EAAE,MAAM,CAAC;QAClB,iBAAiB,EAAE,MAAM,CAAC;KAC3B,CAAC;IAgFI,gBAAgB,CACpB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,eAAe,EAAE,EAC3B,wBAAwB,EAAE,MAAM,EAChC,SAAS,EAAE,MAAM,EACjB,4BAA4B,EAAE,MAAM,EACpC,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,sBAAsB,CAAC;IAiH5B,6CAA6C,CACjD,QAAQ,EAAE,eAAe,EAAE,GAC1B,OAAO,CAAC;QACT,cAAc,EAAE,OAAO,EAAE,CAAC;QAC1B,gBAAgB,EAAE,eAAe,EAAE,CAAC;QACpC,UAAU,EAAE,MAAM,EAAE,CAAC;KACtB,CAAC;IAqCI,wCAAwC,CAC5C,WAAW,EAAE,MAAM,EACnB,aAAa,EAAE,qBAAqB,EACpC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAAE,GACnB,OAAO,CAAC,OAAO,CAAC;IA2Bb,yBAAyB,CAC7B,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,oBAAoB,CAAC;IAe1B,8BAA8B,CAClC,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC;CAc7C"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { stringToHex, u8aToHex } from "@polkadot/util";
|
|
2
2
|
import { randomAsHex, signatureVerify } from "@polkadot/util-crypto";
|
|
3
3
|
import { ProsopoEnvError } from "@prosopo/common";
|
|
4
4
|
import { compareCaptchaSolutions, computePendingRequestHash, parseAndSortCaptchaSolutions, } from "@prosopo/datasets";
|
|
@@ -27,7 +27,7 @@ export class ImgCaptchaManager {
|
|
|
27
27
|
}
|
|
28
28
|
return captchaDocs;
|
|
29
29
|
}
|
|
30
|
-
async getRandomCaptchasAndRequestHash(datasetId, userAccount) {
|
|
30
|
+
async getRandomCaptchasAndRequestHash(datasetId, userAccount, ipAddress) {
|
|
31
31
|
const dataset = await this.db.getDatasetDetails(datasetId);
|
|
32
32
|
if (!dataset) {
|
|
33
33
|
throw new ProsopoEnvError("DATABASE.DATASET_GET_FAILED", {
|
|
@@ -52,36 +52,36 @@ export class ImgCaptchaManager {
|
|
|
52
52
|
const salt = randomAsHex();
|
|
53
53
|
const requestHash = computePendingRequestHash(captchas.map((c) => c.captchaId), userAccount, salt);
|
|
54
54
|
const currentTime = Date.now();
|
|
55
|
-
const
|
|
55
|
+
const signedRequestHash = u8aToHex(this.pair.sign(stringToHex(requestHash)));
|
|
56
56
|
const timeLimit = captchas
|
|
57
57
|
.map((captcha) => captcha.timeLimitMs || DEFAULT_IMAGE_CAPTCHA_TIMEOUT)
|
|
58
58
|
.reduce((a, b) => a + b, 0);
|
|
59
59
|
const deadlineTs = timeLimit + currentTime;
|
|
60
60
|
const currentBlockNumber = 0;
|
|
61
|
-
await this.db.storeDappUserPending(userAccount, requestHash, salt, deadlineTs,
|
|
61
|
+
await this.db.storeDappUserPending(userAccount, requestHash, salt, deadlineTs, currentTime, ipAddress);
|
|
62
62
|
return {
|
|
63
63
|
captchas,
|
|
64
64
|
requestHash,
|
|
65
65
|
timestamp: currentTime,
|
|
66
|
-
|
|
66
|
+
signedRequestHash,
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
async dappUserSolution(userAccount, dappAccount, requestHash, captchas,
|
|
70
|
-
const verification = signatureVerify(stringToHex(requestHash),
|
|
69
|
+
async dappUserSolution(userAccount, dappAccount, requestHash, captchas, userRequestHashSignature, timestamp, providerRequestHashSignature, ipAddress) {
|
|
70
|
+
const verification = signatureVerify(stringToHex(requestHash), userRequestHashSignature, userAccount);
|
|
71
71
|
if (!verification.isValid) {
|
|
72
|
-
this.logger.info("Invalid requestHash signature");
|
|
72
|
+
this.logger.info("Invalid user requestHash signature");
|
|
73
73
|
throw new ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
|
|
74
74
|
context: { failedFuncName: this.dappUserSolution.name, userAccount },
|
|
75
75
|
});
|
|
76
76
|
}
|
|
77
|
-
const
|
|
78
|
-
if (!
|
|
79
|
-
this.logger.info("Invalid
|
|
77
|
+
const providerRequestHashSignatureVerify = signatureVerify(stringToHex(requestHash.toString()), providerRequestHashSignature, this.pair.address);
|
|
78
|
+
if (!providerRequestHashSignatureVerify.isValid) {
|
|
79
|
+
this.logger.info("Invalid provider requestHash signature");
|
|
80
80
|
throw new ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
|
|
81
81
|
context: {
|
|
82
82
|
failedFuncName: this.dappUserSolution.name,
|
|
83
83
|
userAccount,
|
|
84
|
-
error: "
|
|
84
|
+
error: "requestHash signature is invalid",
|
|
85
85
|
},
|
|
86
86
|
});
|
|
87
87
|
}
|
|
@@ -92,7 +92,6 @@ export class ImgCaptchaManager {
|
|
|
92
92
|
const pendingRecord = await this.db.getDappUserPending(requestHash);
|
|
93
93
|
const unverifiedCaptchaIds = captchas.map((captcha) => captcha.captchaId);
|
|
94
94
|
const pendingRequest = await this.validateDappUserSolutionRequestIsPending(requestHash, pendingRecord, userAccount, unverifiedCaptchaIds);
|
|
95
|
-
console.log("Pending request", pendingRequest);
|
|
96
95
|
if (pendingRequest) {
|
|
97
96
|
const { storedCaptchas, receivedCaptchas, captchaIds } = await this.validateReceivedCaptchasAgainstStoredCaptchas(captchas);
|
|
98
97
|
const { tree, commitmentId } = buildTreeAndGetCommitmentId(receivedCaptchas);
|
|
@@ -102,26 +101,21 @@ export class ImgCaptchaManager {
|
|
|
102
101
|
context: { failedFuncName: this.dappUserSolution.name },
|
|
103
102
|
});
|
|
104
103
|
}
|
|
105
|
-
const userSignature = hexToU8a(requestHashSignature);
|
|
106
104
|
await this.db.updateDappUserPendingStatus(requestHash);
|
|
107
105
|
const commit = {
|
|
108
106
|
id: commitmentId,
|
|
109
107
|
userAccount: userAccount,
|
|
110
|
-
|
|
108
|
+
dappAccount,
|
|
111
109
|
providerAccount: this.pair.address,
|
|
112
110
|
datasetId,
|
|
113
|
-
status: CaptchaStatus.pending,
|
|
114
|
-
userSignature:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
processed: false,
|
|
118
|
-
batched: false,
|
|
119
|
-
stored: false,
|
|
111
|
+
result: { status: CaptchaStatus.pending },
|
|
112
|
+
userSignature: userRequestHashSignature,
|
|
113
|
+
userSubmitted: true,
|
|
114
|
+
serverChecked: false,
|
|
120
115
|
requestedAtTimestamp: timestamp,
|
|
116
|
+
ipAddress,
|
|
121
117
|
};
|
|
122
118
|
await this.db.storeDappUserSolution(receivedCaptchas, commit);
|
|
123
|
-
console.log(receivedCaptchas);
|
|
124
|
-
console.log(storedCaptchas);
|
|
125
119
|
if (compareCaptchaSolutions(receivedCaptchas, storedCaptchas)) {
|
|
126
120
|
response = {
|
|
127
121
|
captchas: captchaIds.map((id) => ({
|
|
@@ -133,6 +127,7 @@ export class ImgCaptchaManager {
|
|
|
133
127
|
await this.db.approveDappUserCommitment(commitmentId);
|
|
134
128
|
}
|
|
135
129
|
else {
|
|
130
|
+
await this.db.disapproveDappUserCommitment(commitmentId, "CAPTCHA.INVALID_SOLUTION");
|
|
136
131
|
response = {
|
|
137
132
|
captchas: captchaIds.map((id) => ({
|
|
138
133
|
captchaId: id,
|
|
@@ -197,11 +192,11 @@ export class ImgCaptchaManager {
|
|
|
197
192
|
}
|
|
198
193
|
return dappUserSolution;
|
|
199
194
|
}
|
|
200
|
-
async getDappUserCommitmentByAccount(userAccount) {
|
|
201
|
-
const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(userAccount);
|
|
195
|
+
async getDappUserCommitmentByAccount(userAccount, dappAccount) {
|
|
196
|
+
const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(userAccount, dappAccount);
|
|
202
197
|
if (dappUserSolutions.length > 0) {
|
|
203
198
|
for (const dappUserSolution of dappUserSolutions) {
|
|
204
|
-
if (dappUserSolution.status === CaptchaStatus.approved) {
|
|
199
|
+
if (dappUserSolution.result.status === CaptchaStatus.approved) {
|
|
205
200
|
return dappUserSolution;
|
|
206
201
|
}
|
|
207
202
|
}
|