@prosopo/provider 0.2.32 → 0.2.36

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.
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const z = require("zod");
4
+ const types = require("@prosopo/types");
5
+ require("../index.cjs");
6
+ const typesReturns = require("@prosopo/captcha-contract/types-returns");
7
+ const common = require("@prosopo/common");
8
+ const express = require("express");
9
+ const authMiddleware = require("./authMiddleware.cjs");
10
+ const contract = require("@prosopo/contract");
11
+ const tasks = require("../tasks/tasks.cjs");
12
+ const commitments = require("../batch/commitments.cjs");
13
+ function _interopNamespaceDefault(e) {
14
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
15
+ if (e) {
16
+ for (const k in e) {
17
+ if (k !== "default") {
18
+ const d = Object.getOwnPropertyDescriptor(e, k);
19
+ Object.defineProperty(n, k, d.get ? d : {
20
+ enumerable: true,
21
+ get: () => e[k]
22
+ });
23
+ }
24
+ }
25
+ }
26
+ n.default = e;
27
+ return Object.freeze(n);
28
+ }
29
+ const z__namespace = /* @__PURE__ */ _interopNamespaceDefault(z);
30
+ const apiBatchCommitConfig = {
31
+ interval: 0,
32
+ maxBatchExtrinsicPercentage: 59
33
+ };
34
+ function prosopoAdminRouter(env) {
35
+ const router = express.Router();
36
+ const tasks$1 = new tasks.Tasks(env);
37
+ router.use(authMiddleware.authMiddleware(tasks$1, env));
38
+ router.post(types.AdminApiPaths.BatchCommit, async (req, res, next) => {
39
+ if (env.db) {
40
+ try {
41
+ const batchCommitter = new commitments.BatchCommitmentsTask(
42
+ apiBatchCommitConfig,
43
+ env.getContractInterface(),
44
+ env.db,
45
+ 0n,
46
+ env.logger
47
+ );
48
+ const result = await batchCommitter.run();
49
+ console.info(`Batch commit complete: ${result}`);
50
+ res.status(200).send(result);
51
+ } catch (err) {
52
+ console.error(err);
53
+ res.status(500).send(err);
54
+ }
55
+ } else {
56
+ console.error("No database configured");
57
+ res.status(500).send("No database configured");
58
+ }
59
+ });
60
+ router.post(types.AdminApiPaths.UpdateDataset, async (req, res, next) => {
61
+ try {
62
+ const result = await tasks$1.providerSetDataset(req.body);
63
+ console.info(`Dataset update complete: ${result}`);
64
+ res.status(200).send(result);
65
+ } catch (err) {
66
+ console.error(err);
67
+ res.status(500).send(err);
68
+ }
69
+ });
70
+ router.post(types.AdminApiPaths.ProviderDeregister, async (req, res, next) => {
71
+ var _a;
72
+ try {
73
+ const address = (_a = env.pair) == null ? void 0 : _a.address;
74
+ if (!address) {
75
+ throw new common.ProsopoEnvError("DEVELOPER.MISSING_ENV_VARIABLE", { context: { error: "No address" } });
76
+ }
77
+ await tasks$1.contract.tx.providerDeregister();
78
+ } catch (err) {
79
+ console.error(err);
80
+ res.status(500).send(err);
81
+ }
82
+ });
83
+ router.post(types.AdminApiPaths.ProviderUpdate, async (req, res, next) => {
84
+ try {
85
+ const { url, fee, payee, value, address } = z__namespace.object({
86
+ url: z__namespace.string(),
87
+ fee: z__namespace.number().optional(),
88
+ payee: z__namespace.nativeEnum(typesReturns.Payee).optional(),
89
+ value: z__namespace.number().optional(),
90
+ address: z__namespace.string()
91
+ }).parse(req.body);
92
+ const provider = (await tasks$1.contract.query.getProvider(address, {})).value.unwrap().unwrap();
93
+ if (provider && (url || fee || payee || value)) {
94
+ const urlConverted = url ? Array.from(new common.UrlConverter().encode(url.toString())) : provider.url;
95
+ await contract.wrapQuery(tasks$1.contract.query.providerUpdate, tasks$1.contract.query)(
96
+ urlConverted,
97
+ fee || provider.fee,
98
+ payee || provider.payee,
99
+ { value: value || 0 }
100
+ );
101
+ const result = await tasks$1.contract.tx.providerUpdate(
102
+ urlConverted,
103
+ fee || provider.fee,
104
+ payee || provider.payee,
105
+ { value: value || 0 }
106
+ );
107
+ console.info(JSON.stringify(result, null, 2));
108
+ }
109
+ } catch (err) {
110
+ console.error(err);
111
+ res.status(500).send(err);
112
+ }
113
+ });
114
+ return router;
115
+ }
116
+ exports.prosopoAdminRouter = prosopoAdminRouter;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const common = require("@prosopo/common");
4
+ const util = require("@polkadot/util");
5
+ const authMiddleware = (tasks, env) => {
6
+ return async (req, res, next) => {
7
+ try {
8
+ const { signature, blocknumber } = extractHeaders(req);
9
+ if (!env.pair) {
10
+ throw new common.ProsopoEnvError("CONTRACT.CANNOT_FIND_KEYPAIR");
11
+ }
12
+ verifyEnvironmentKeyPair(env);
13
+ await verifyBlockNumber(blocknumber, tasks);
14
+ verifySignature(signature, blocknumber, env.pair);
15
+ next();
16
+ } catch (err) {
17
+ console.error("Auth Middleware Error:", err);
18
+ res.status(401).json({ error: "Unauthorized", message: err });
19
+ }
20
+ };
21
+ };
22
+ const extractHeaders = (req) => {
23
+ const signature = req.headers.signature;
24
+ const blocknumber = req.headers.blocknumber;
25
+ if (!signature || !blocknumber) {
26
+ throw new common.ProsopoApiError("CONTRACT.INVALID_DATA_FORMAT", {
27
+ context: { error: "Missing signature or block number" }
28
+ });
29
+ }
30
+ if (Array.isArray(signature) || Array.isArray(blocknumber) || !util.isHex(signature)) {
31
+ throw new common.ProsopoApiError("CONTRACT.INVALID_DATA_FORMAT", { context: { error: "Invalid header format" } });
32
+ }
33
+ return { signature, blocknumber };
34
+ };
35
+ const verifyEnvironmentKeyPair = (env) => {
36
+ if (!env.pair) {
37
+ throw new common.ProsopoEnvError("CONTRACT.CANNOT_FIND_KEYPAIR");
38
+ }
39
+ };
40
+ const verifyBlockNumber = async (blockNumber, tasks) => {
41
+ const parsedBlockNumber = parseInt(blockNumber);
42
+ const currentBlockNumber = await tasks.getCurrentBlockNumber();
43
+ if (isNaN(parsedBlockNumber) || parsedBlockNumber < currentBlockNumber - 500 || parsedBlockNumber > currentBlockNumber) {
44
+ throw new common.ProsopoApiError("API.BAD_REQUEST", {
45
+ context: {
46
+ error: `Invalid block number ${parsedBlockNumber}, current block number is ${currentBlockNumber}`
47
+ }
48
+ });
49
+ }
50
+ };
51
+ const verifySignature = (signature, blockNumber, pair) => {
52
+ const u8Sig = util.hexToU8a(signature);
53
+ if (!pair.verify(blockNumber, u8Sig, pair.publicKey)) {
54
+ throw new common.ProsopoApiError("GENERAL.INVALID_SIGNATURE", { context: { error: "Signature verification failed" } });
55
+ }
56
+ };
57
+ exports.authMiddleware = authMiddleware;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const types = require("@prosopo/types");
4
- const captcha = require("../contracts/captcha/dist/types-returns/captcha.cjs");
4
+ const typesReturns = require("@prosopo/captcha-contract/types-returns");
5
5
  const common = require("@prosopo/common");
6
6
  const tasks = require("../tasks/tasks.cjs");
7
7
  const util = require("../util.cjs");
@@ -18,7 +18,9 @@ function prosopoRouter(env) {
18
18
  const { blockNumber, datasetId, user, dapp } = types.CaptchaRequestBody.parse(req.params);
19
19
  const api = env.api;
20
20
  if (api === void 0) {
21
- throw new Error("api not setup");
21
+ throw new common.ProsopoApiError("DEVELOPER.METHOD_NOT_IMPLEMENTED", {
22
+ context: { error: "api not setup", env }
23
+ });
22
24
  }
23
25
  address.validateAddress(user, false, api.registry.chainSS58);
24
26
  const blockNumberParsed = util.parseBlockNumber(blockNumber);
@@ -36,7 +38,7 @@ function prosopoRouter(env) {
36
38
  };
37
39
  return res.json(captchaResponse);
38
40
  } catch (err) {
39
- return next(new common.ProsopoApiError(err, void 0, 400));
41
+ return next(new common.ProsopoApiError("API.BAD_REQUEST", { context: { error: err, errorCode: 400 } }));
40
42
  }
41
43
  }
42
44
  );
@@ -45,7 +47,7 @@ function prosopoRouter(env) {
45
47
  try {
46
48
  parsed = types.CaptchaSolutionBody.parse(req.body);
47
49
  } catch (err) {
48
- return next(new common.ProsopoApiError(err, void 0, 400));
50
+ return next(new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", { context: { errorCode: 400, error: err } }));
49
51
  }
50
52
  try {
51
53
  const result = await tasks$1.dappUserSolution(
@@ -60,7 +62,7 @@ function prosopoRouter(env) {
60
62
  ...result
61
63
  });
62
64
  } catch (err) {
63
- return next(new common.ProsopoApiError(err, void 0, 400));
65
+ return next(new common.ProsopoApiError("API.UNKNOWN", { context: { errorCode: 400, error: err } }));
64
66
  }
65
67
  });
66
68
  router.post(types.ApiPaths.VerifyCaptchaSolution, async (req, res, next) => {
@@ -68,34 +70,38 @@ function prosopoRouter(env) {
68
70
  try {
69
71
  parsed = types.VerifySolutionBody.parse(req.body);
70
72
  } catch (err) {
71
- return next(new common.ProsopoApiError(err, void 0, 400));
73
+ return next(new common.ProsopoApiError("CAPTCHA.PARSE_ERROR", { context: { errorCode: 400, error: err } }));
72
74
  }
73
75
  try {
74
- let solution;
75
- let statusMessage = "API.USER_NOT_VERIFIED";
76
- if (!parsed.commitmentId) {
77
- solution = await tasks$1.getDappUserCommitmentByAccount(parsed.user);
78
- } else {
79
- solution = await tasks$1.getDappUserCommitmentById(parsed.commitmentId);
76
+ const solution = await (parsed.commitmentId ? tasks$1.getDappUserCommitmentById(parsed.commitmentId) : tasks$1.getDappUserCommitmentByAccount(parsed.user));
77
+ if (!solution) {
78
+ return res.json({ status: req.t("API.USER_NOT_VERIFIED"), solutionApproved: false });
80
79
  }
81
- if (solution) {
82
- let approved = false;
83
- if (solution.status === captcha.CaptchaStatus.approved) {
84
- statusMessage = "API.USER_VERIFIED";
85
- approved = true;
80
+ if (parsed.maxVerifiedTime) {
81
+ const currentBlockNumber = await tasks$1.getCurrentBlockNumber();
82
+ const blockTimeMs = await tasks$1.getBlockTimeMs();
83
+ const timeSinceCompletion = (currentBlockNumber - solution.completedAt) * blockTimeMs;
84
+ if (timeSinceCompletion > parsed.maxVerifiedTime) {
85
+ return res.json({ status: req.t("API.USER_NOT_VERIFIED"), solutionApproved: false });
86
86
  }
87
- return res.json({
88
- status: req.t(statusMessage),
89
- solutionApproved: approved,
90
- commitmentId: solution.id
91
- });
92
87
  }
88
+ const isApproved = solution.status === typesReturns.CaptchaStatus.approved;
93
89
  return res.json({
94
- status: req.t(statusMessage),
95
- solutionApproved: false
90
+ status: req.t(isApproved ? "API.USER_VERIFIED" : "API.USER_NOT_VERIFIED"),
91
+ solutionApproved: isApproved,
92
+ commitmentId: solution.id
96
93
  });
97
94
  } catch (err) {
98
- return next(new common.ProsopoApiError(err, void 0, 400));
95
+ return next(new common.ProsopoApiError("API.BAD_REQUEST", { context: { errorCode: 400, error: err } }));
96
+ }
97
+ });
98
+ router.post(types.ApiPaths.SubmitUserEvents, async (req, res, next) => {
99
+ try {
100
+ const { events, accountId } = req.body;
101
+ await tasks$1.saveCaptchaEvent(events, accountId);
102
+ return res.json({ status: "success" });
103
+ } catch (err) {
104
+ return next(new common.ProsopoApiError("API.BAD_REQUEST", { context: { errorCode: 400, error: err } }));
99
105
  }
100
106
  });
101
107
  router.get(types.ApiPaths.GetProviderStatus, async (req, res, next) => {
@@ -103,7 +109,7 @@ function prosopoRouter(env) {
103
109
  const status = await tasks$1.providerStatus();
104
110
  return res.json({ status });
105
111
  } catch (err) {
106
- return next(new common.ProsopoApiError(err, void 0, 400));
112
+ return next(new common.ProsopoApiError("API.BAD_REQUEST", { context: { errorCode: 400, error: err } }));
107
113
  }
108
114
  });
109
115
  router.get(types.ApiPaths.GetProviderDetails, async (req, res, next) => {
@@ -111,7 +117,7 @@ function prosopoRouter(env) {
111
117
  const details = await tasks$1.getProviderDetails();
112
118
  return res.json(details);
113
119
  } catch (err) {
114
- return next(new common.ProsopoApiError(err, void 0, 400));
120
+ return next(new common.ProsopoApiError("API.BAD_REQUEST", { context: { errorCode: 400, error: err } }));
115
121
  }
116
122
  });
117
123
  return router;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const bn = require("@polkadot/util/bn");
4
4
  const types = require("@prosopo/types");
5
+ const common = require("@prosopo/common");
5
6
  const contract = require("@prosopo/contract");
6
7
  const util = require("../util.cjs");
7
8
  const random = require("@polkadot/util-crypto/random");
@@ -44,6 +45,7 @@ class BatchCommitmentsTask {
44
45
  }
45
46
  );
46
47
  }
48
+ return commitments;
47
49
  } catch (e) {
48
50
  const err = e;
49
51
  this.logger.error(e);
@@ -55,6 +57,7 @@ class BatchCommitmentsTask {
55
57
  error: JSON.stringify(e && err.message ? err.message : e)
56
58
  }
57
59
  );
60
+ return err.message;
58
61
  }
59
62
  }
60
63
  }
@@ -119,7 +122,7 @@ class BatchCommitmentsTask {
119
122
  }
120
123
  }
121
124
  if (!extrinsic) {
122
- throw new contract.ProsopoContractError("No extrinsics created");
125
+ throw new common.ProsopoContractError("CONTRACT.TX_ERROR", { context: { error: "No extrinsics created" } });
123
126
  }
124
127
  txs.push(extrinsic);
125
128
  this.logger.info(`${txs.length} transactions will be batched`);
@@ -4,6 +4,7 @@ require("./tasks/index.cjs");
4
4
  const util = require("./util.cjs");
5
5
  require("./batch/index.cjs");
6
6
  const captcha = require("./api/captcha.cjs");
7
+ const admin = require("./api/admin.cjs");
7
8
  const tasks = require("./tasks/tasks.cjs");
8
9
  const calculateSolutions = require("./tasks/calculateSolutions.cjs");
9
10
  const commitments = require("./batch/commitments.cjs");
@@ -15,6 +16,7 @@ exports.promiseQueue = util.promiseQueue;
15
16
  exports.shuffleArray = util.shuffleArray;
16
17
  exports.updateSolutions = util.updateSolutions;
17
18
  exports.prosopoRouter = captcha.prosopoRouter;
19
+ exports.prosopoAdminRouter = admin.prosopoAdminRouter;
18
20
  exports.Tasks = tasks.Tasks;
19
21
  exports.CalculateSolutionsTask = calculateSolutions.CalculateSolutionsTask;
20
22
  exports.BatchCommitmentsTask = commitments.BatchCommitmentsTask;
@@ -60,7 +60,7 @@ class CalculateSolutionsTask extends tasks.Tasks {
60
60
  }
61
61
  return 0;
62
62
  } catch (error) {
63
- throw new common.ProsopoEnvError(error, "GENERAL.CALCULATE_CAPTCHA_SOLUTION");
63
+ throw new common.ProsopoEnvError("GENERAL.CALCULATE_CAPTCHA_SOLUTION", { context: { error } });
64
64
  }
65
65
  }
66
66
  }
@@ -1,24 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
3
  const datasets = require("@prosopo/datasets");
4
- const captcha = require("../contracts/captcha/dist/types-returns/captcha.cjs");
4
+ const typesReturns = require("@prosopo/captcha-contract/types-returns");
5
5
  const common = require("@prosopo/common");
6
6
  const contract = require("@prosopo/contract");
7
7
  const util$1 = require("@prosopo/util");
8
8
  const hex = require("@polkadot/util/hex");
9
9
  const random = require("@polkadot/util-crypto/random");
10
+ const database = require("@prosopo/database");
10
11
  const util = require("../util.cjs");
11
12
  const signature = require("@polkadot/util-crypto/signature");
12
13
  const string = require("@polkadot/util/string");
13
14
  class Tasks {
14
15
  constructor(env) {
15
16
  if (!env.contractInterface) {
16
- throw new common.ProsopoEnvError(
17
- "CONTRACT.CONTRACT_UNDEFINED",
18
- this.constructor.name,
19
- {},
20
- { contractAddress: env.contractAddress }
21
- );
17
+ throw new common.ProsopoEnvError("CONTRACT.CONTRACT_UNDEFINED", {
18
+ context: { failedFuncName: this.constructor.name, contractAddress: env.contractAddress }
19
+ });
22
20
  }
23
21
  this.config = env.config;
24
22
  this.contract = env.contractInterface;
@@ -35,18 +33,26 @@ class Tasks {
35
33
  async providerSetDataset(datasetRaw) {
36
34
  var _a;
37
35
  if (datasetRaw.captchas.length < this.config.captchas.solved.count + this.config.captchas.unsolved.count) {
38
- throw new common.ProsopoEnvError("DATASET.CAPTCHAS_COUNT_LESS_THAN_CONFIGURED", this.providerSetDataset.name);
36
+ throw new common.ProsopoEnvError("DATASET.CAPTCHAS_COUNT_LESS_THAN_CONFIGURED", {
37
+ context: { failedFuncName: this.providerSetDataset.name }
38
+ });
39
39
  }
40
- const solutions = datasetRaw.captchas.map((captcha2) => captcha2.solution ? 1 : 0).reduce((partialSum, b) => partialSum + b, 0);
40
+ const solutions = datasetRaw.captchas.map((captcha) => captcha.solution ? 1 : 0).reduce((partialSum, b) => partialSum + b, 0);
41
41
  if (solutions < this.config.captchas.solved.count) {
42
- throw new common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", this.providerSetDataset.name);
42
+ throw new common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", {
43
+ context: { failedFuncName: this.providerSetDataset.name }
44
+ });
43
45
  }
44
46
  if (solutions < this.config.captchas.unsolved.count) {
45
- throw new common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", this.providerSetDataset.name);
47
+ throw new common.ProsopoEnvError("DATASET.SOLUTIONS_COUNT_LESS_THAN_CONFIGURED", {
48
+ context: { failedFuncName: this.providerSetDataset.name }
49
+ });
46
50
  }
47
51
  const dataset = await datasets.buildDataset(datasetRaw);
48
52
  if (!dataset.datasetId || !dataset.datasetContentId) {
49
- throw new common.ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", this.providerSetDataset.name);
53
+ throw new common.ProsopoEnvError("DATASET.DATASET_ID_UNDEFINED", {
54
+ context: { failedFuncName: this.providerSetDataset.name }
55
+ });
50
56
  }
51
57
  await ((_a = this.db) == null ? void 0 : _a.storeDataset(dataset));
52
58
  await contract.wrapQuery(this.contract.query.providerSetDataset, this.contract.query)(
@@ -69,25 +75,22 @@ class Tasks {
69
75
  const captchaDocs = await this.db.getRandomCaptcha(solved, datasetId, size);
70
76
  if (captchaDocs) {
71
77
  const captchas = [];
72
- for (const captcha2 of captchaDocs) {
78
+ for (const captcha of captchaDocs) {
73
79
  const datasetDetails = await this.db.getDatasetDetails(datasetId);
74
80
  const tree = new datasets.CaptchaMerkleTree();
75
81
  if (datasetDetails.contentTree) {
76
82
  tree.layers = datasetDetails.contentTree;
77
- const proof = tree.proof(captcha2.captchaContentId);
78
- delete captcha2.solution;
79
- captcha2.items = util.shuffleArray(captcha2.items);
80
- captchas.push({ captcha: captcha2, proof });
83
+ const proof = tree.proof(captcha.captchaContentId);
84
+ delete captcha.solution;
85
+ captcha.items = util.shuffleArray(captcha.items);
86
+ captchas.push({ captcha, proof });
81
87
  }
82
88
  }
83
89
  return captchas;
84
90
  }
85
- throw new common.ProsopoEnvError(
86
- "DATABASE.CAPTCHA_GET_FAILED",
87
- this.getCaptchaWithProof.name,
88
- {},
89
- { datasetId, solved, size }
90
- );
91
+ throw new common.ProsopoEnvError("DATABASE.CAPTCHA_GET_FAILED", {
92
+ context: { failedFuncName: this.getCaptchaWithProof.name, datasetId, solved, size }
93
+ });
91
94
  }
92
95
  /**
93
96
  * Validate and store the text captcha solution(s) from the Dapp User in a web2 environment
@@ -100,11 +103,15 @@ class Tasks {
100
103
  */
101
104
  async dappUserSolution(userAccount, dappAccount, requestHash, captchas, signature$1) {
102
105
  if (!await this.dappIsActive(dappAccount)) {
103
- throw new common.ProsopoEnvError("CONTRACT.DAPP_NOT_ACTIVE", this.getPaymentInfo.name, {}, { dappAccount });
106
+ throw new common.ProsopoEnvError("CONTRACT.DAPP_NOT_ACTIVE", {
107
+ context: { failedFuncName: this.getPaymentInfo.name, dappAccount }
108
+ });
104
109
  }
105
110
  const verification = signature.signatureVerify(string.stringToHex(requestHash), signature$1, userAccount);
106
111
  if (!verification.isValid) {
107
- throw new common.ProsopoEnvError("GENERAL.INVALID_SIGNATURE", this.dappUserSolution.name, {}, { userAccount });
112
+ throw new common.ProsopoEnvError("GENERAL.INVALID_SIGNATURE", {
113
+ context: { failedFuncName: this.dappUserSolution.name, userAccount }
114
+ });
108
115
  }
109
116
  let response = {
110
117
  captchas: [],
@@ -129,7 +136,7 @@ class Tasks {
129
136
  dappContract: dappAccount,
130
137
  providerAccount: this.contract.pair.address,
131
138
  datasetId: provider.datasetId.toString(),
132
- status: captcha.CaptchaStatus.pending,
139
+ status: typesReturns.CaptchaStatus.pending,
133
140
  userSignature: Array.from(userSignature),
134
141
  requestedAt: pendingRecord.requestedAtBlock,
135
142
  // TODO is this correct or should it be block number?
@@ -186,23 +193,23 @@ class Tasks {
186
193
  */
187
194
  async validateReceivedCaptchasAgainstStoredCaptchas(captchas) {
188
195
  const receivedCaptchas = datasets.parseAndSortCaptchaSolutions(captchas);
189
- const captchaIds = receivedCaptchas.map((captcha2) => captcha2.captchaId);
196
+ const captchaIds = receivedCaptchas.map((captcha) => captcha.captchaId);
190
197
  const storedCaptchas = await this.db.getCaptchaById(captchaIds);
191
198
  if (!storedCaptchas || receivedCaptchas.length !== storedCaptchas.length) {
192
- throw new common.ProsopoEnvError(
193
- "CAPTCHA.INVALID_CAPTCHA_ID",
194
- this.validateReceivedCaptchasAgainstStoredCaptchas.name,
195
- {},
196
- captchas
197
- );
199
+ throw new common.ProsopoEnvError("CAPTCHA.INVALID_CAPTCHA_ID", {
200
+ context: {
201
+ failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
202
+ captchas
203
+ }
204
+ });
198
205
  }
199
- if (!storedCaptchas.every((captcha2) => captcha2.datasetId === util$1.at(storedCaptchas, 0).datasetId)) {
200
- throw new common.ProsopoEnvError(
201
- "CAPTCHA.DIFFERENT_DATASET_IDS",
202
- this.validateReceivedCaptchasAgainstStoredCaptchas.name,
203
- {},
204
- captchas
205
- );
206
+ if (!storedCaptchas.every((captcha) => captcha.datasetId === util$1.at(storedCaptchas, 0).datasetId)) {
207
+ throw new common.ProsopoEnvError("CAPTCHA.DIFFERENT_DATASET_IDS", {
208
+ context: {
209
+ failedFuncName: this.validateReceivedCaptchasAgainstStoredCaptchas.name,
210
+ captchas
211
+ }
212
+ });
206
213
  }
207
214
  return { storedCaptchas, receivedCaptchas, captchaIds };
208
215
  }
@@ -214,16 +221,16 @@ class Tasks {
214
221
  async buildTreeAndGetCommitmentId(captchaSolutions) {
215
222
  var _a;
216
223
  const tree = new datasets.CaptchaMerkleTree();
217
- const solutionsHashed = captchaSolutions.map((captcha2) => datasets.computeCaptchaSolutionHash(captcha2));
224
+ const solutionsHashed = captchaSolutions.map((captcha) => datasets.computeCaptchaSolutionHash(captcha));
218
225
  tree.build(solutionsHashed);
219
226
  const commitmentId = (_a = tree.root) == null ? void 0 : _a.hash;
220
227
  if (!commitmentId) {
221
- throw new common.ProsopoEnvError(
222
- "CONTRACT.CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST",
223
- this.buildTreeAndGetCommitmentId.name,
224
- {},
225
- { commitmentId }
226
- );
228
+ throw new common.ProsopoEnvError("CONTRACT.CAPTCHA_SOLUTION_COMMITMENT_DOES_NOT_EXIST", {
229
+ context: {
230
+ failedFuncName: this.buildTreeAndGetCommitmentId.name,
231
+ commitmentId
232
+ }
233
+ });
227
234
  }
228
235
  return { tree, commitmentId };
229
236
  }
@@ -274,7 +281,7 @@ class Tasks {
274
281
  salt
275
282
  );
276
283
  const currentTime = Date.now();
277
- const timeLimit = captchas.map((captcha2) => captcha2.captcha.timeLimitMs || 3e4).reduce((a, b) => a + b, 0);
284
+ const timeLimit = captchas.map((captcha) => captcha.captcha.timeLimitMs || 3e4).reduce((a, b) => a + b, 0);
278
285
  const deadlineTs = timeLimit + currentTime;
279
286
  const currentBlockNumber = await contract.getBlockNumber(this.contract.api);
280
287
  await this.db.storeDappUserPending(userAccount, requestHash, salt, deadlineTs, currentBlockNumber.toNumber());
@@ -304,23 +311,23 @@ class Tasks {
304
311
  async validateProviderWasRandomlyChosen(userAccount, dappContractAccount, datasetId, blockNumber) {
305
312
  const contract2 = await this.contract.contract;
306
313
  if (!contract2) {
307
- throw new common.ProsopoEnvError("CONTRACT.CONTRACT_UNDEFINED", this.validateProviderWasRandomlyChosen.name);
314
+ throw new common.ProsopoEnvError("CONTRACT.CONTRACT_UNDEFINED", {
315
+ context: { failedFuncName: this.validateProviderWasRandomlyChosen.name }
316
+ });
308
317
  }
309
318
  const header = await contract2.api.rpc.chain.getHeader();
310
319
  const isBlockNoValid = await this.isRecentBlock(contract2, header, blockNumber);
311
320
  if (!isBlockNoValid) {
312
- throw new common.ProsopoEnvError(
313
- "CAPTCHA.INVALID_BLOCK_NO",
314
- this.validateProviderWasRandomlyChosen.name,
315
- {},
316
- {
321
+ throw new common.ProsopoEnvError("CAPTCHA.INVALID_BLOCK_NO", {
322
+ context: {
323
+ failedFuncName: this.validateProviderWasRandomlyChosen.name,
317
324
  userAccount,
318
325
  dappContractAccount,
319
326
  datasetId,
320
327
  header,
321
328
  blockNumber
322
329
  }
323
- );
330
+ });
324
331
  }
325
332
  const block = await contract2.api.rpc.chain.getBlockHash(blockNumber);
326
333
  const randomProviderAndBlockNo = await this.contract.queryAtBlock(
@@ -329,12 +336,12 @@ class Tasks {
329
336
  [userAccount, dappContractAccount]
330
337
  );
331
338
  if (datasetId.toString().localeCompare(randomProviderAndBlockNo.provider.datasetId.toString())) {
332
- throw new common.ProsopoEnvError(
333
- "DATASET.INVALID_DATASET_ID",
334
- this.validateProviderWasRandomlyChosen.name,
335
- {},
336
- randomProviderAndBlockNo
337
- );
339
+ throw new common.ProsopoEnvError("DATASET.INVALID_DATASET_ID", {
340
+ context: {
341
+ failedFuncName: this.validateProviderWasRandomlyChosen.name,
342
+ randomProviderAndBlockNo
343
+ }
344
+ });
338
345
  }
339
346
  }
340
347
  /**
@@ -368,12 +375,12 @@ class Tasks {
368
375
  async getDappUserCommitmentById(commitmentId) {
369
376
  const dappUserSolution = await this.db.getDappUserCommitmentById(commitmentId);
370
377
  if (!dappUserSolution) {
371
- throw new common.ProsopoEnvError(
372
- "CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND",
373
- this.getDappUserCommitmentById.name,
374
- {},
375
- { commitmentId }
376
- );
378
+ throw new common.ProsopoEnvError("CAPTCHA.DAPP_USER_SOLUTION_NOT_FOUND", {
379
+ context: {
380
+ failedFuncName: this.getDappUserCommitmentById.name,
381
+ commitmentId
382
+ }
383
+ });
377
384
  }
378
385
  return dappUserSolution;
379
386
  }
@@ -382,7 +389,7 @@ class Tasks {
382
389
  const dappUserSolutions = await this.db.getDappUserCommitmentByAccount(userAccount);
383
390
  if (dappUserSolutions.length > 0) {
384
391
  for (const dappUserSolution of dappUserSolutions) {
385
- if (dappUserSolution.status === captcha.CaptchaStatus.approved) {
392
+ if (dappUserSolution.status === typesReturns.CaptchaStatus.approved) {
386
393
  return dappUserSolution;
387
394
  }
388
395
  }
@@ -391,11 +398,35 @@ class Tasks {
391
398
  }
392
399
  /* Returns public details of provider */
393
400
  async getProviderDetails() {
394
- return await contract.wrapQuery(this.contract.query.getProvider, this.contract.query)(this.contract.pair.address);
401
+ const provider = await contract.wrapQuery(
402
+ this.contract.query.getProvider,
403
+ this.contract.query
404
+ )(this.contract.pair.address);
405
+ const dbConnectionOk = await this.getCaptchaWithProof(provider.datasetId, true, 1).then(() => true).catch(() => false);
406
+ return { provider, dbConnectionOk };
395
407
  }
396
- /** Get the dataset from the databse */
408
+ /** Get the dataset from the database */
397
409
  async getProviderDataset(datasetId) {
398
410
  return await this.db.getDataset(datasetId);
399
411
  }
412
+ /**
413
+ * Get the current block number
414
+ */
415
+ async getCurrentBlockNumber() {
416
+ return (await contract.getBlockNumber(this.contract.api)).toNumber();
417
+ }
418
+ /**
419
+ * Get the current block time in milliseconds
420
+ */
421
+ async getBlockTimeMs() {
422
+ const blockTime = this.contract.api.consts.babe.expectedBlockTime;
423
+ return blockTime.toNumber();
424
+ }
425
+ async saveCaptchaEvent(events, accountId) {
426
+ if (!this.config.mongoAtlasUri) {
427
+ return;
428
+ }
429
+ await database.saveCaptchaEvent(events, accountId, this.config.mongoAtlasUri);
430
+ }
400
431
  }
401
432
  exports.Tasks = Tasks;