From aeb257b2c6221779983355869a12c40281e30ad4 Mon Sep 17 00:00:00 2001 From: Kosta Korenkov Date: Wed, 19 Feb 2020 02:15:34 +0800 Subject: [PATCH] refactor: reducing codebase by reusing existing solutions - server.js is all repetitive, refactored into common logic - proposed to refactor parameter checks to use off-the-shelf JSON Schema validator --- proof-data-provider/methods.js | 64 -------- proof-data-provider/methods/getProofByKey.js | 62 ++++++++ proof-data-provider/methods/utils.js | 33 ++++ proof-data-provider/package.json | 1 + proof-data-provider/server.js | 154 +++---------------- proof-data-provider/yarn.lock | 5 + 6 files changed, 125 insertions(+), 194 deletions(-) create mode 100644 proof-data-provider/methods/getProofByKey.js create mode 100644 proof-data-provider/methods/utils.js diff --git a/proof-data-provider/methods.js b/proof-data-provider/methods.js index 36384a0..3674537 100644 --- a/proof-data-provider/methods.js +++ b/proof-data-provider/methods.js @@ -885,70 +885,6 @@ async function checkParamsUtype2(params) { inputErrors.utype2 = checkParamsUtype2; -// Function for checking input params for method getProofByKey -async function checkParamsGp1(params) { - const result = { - error: false, - message: null - }; - // check if the params have only two keys - if (Object.keys(params).length > 2) { - result.error = true; - result.message = - 'Object "params" should have only two properties - "index" and "key".'; - return result; - } - // check if the keys of params are index and key (can be improved with more detailed messages) - if ( - !Object.prototype.hasOwnProperty.call(params, "index") || - !Object.prototype.hasOwnProperty.call(params, "key") - ) { - result.error = true; - result.message = - 'There is no required properties "index" and/or "key" in object "params".'; - return result; - } - // check data types of params keys - if (typeof params.index !== "number" || typeof params.key !== "string") { - if (typeof params.index === "number") { - result.error = true; - result.message = 'Invalid data type of "key". Must be a string.'; - return result; - } - result.error = true; - result.message = 'Invalid data type of "index". Must be a number.'; - return result; - } - // check if the item by input's index exist - const item = await managerDB.getItemByIndex(params.index); - if (!item) { - result.error = true; - result.message = `Invalid index. There is no tree with such ${params.index} index.`; - return result; - } - - // check key value - const { depth } = item; - const maxnumber = BigInt(2 ** depth) - BigInt(1); - - let bN; - try { - bN = BigInt(params.key); - } catch (e) { - result.error = true; - result.message = `This "${params.key}" key is not a number.`; - return result; - } - if (bN > maxnumber) { - result.error = true; - result.message = `This "${params.key}" key is out of range of the tree's depth.`; - return result; - } - return result; -} - -inputErrors.gp1 = checkParamsGp1; - // Function for checking params for method getProofByKeys async function checkParamsGp2(params) { const result = { diff --git a/proof-data-provider/methods/getProofByKey.js b/proof-data-provider/methods/getProofByKey.js new file mode 100644 index 0000000..a89bcc4 --- /dev/null +++ b/proof-data-provider/methods/getProofByKey.js @@ -0,0 +1,62 @@ +const SmtLib = require("../helpers/SmtLib.js"); +const managerDB = require("../db.js"); +const { paramsSchema, validate, error } = require("./utils"); + +const schema = paramsSchema("getProofByKey", { + index: { type: "number", required: true }, + key: { type: "string", required: true } +}); + +/** + * Option1. Function for getting one proof for one key. + * @function getProofByKey + * @param {Object} [params] Parameters from the request that was send by client(user) + * @param {Number} [params.index] The index of the existing item (tree), that user got when created a tree into provider's database. + * @param {String} [params.key] The sparse merkle tree's key(path) that proof is required. + * Key must be a number in a range of depth's amount of bits. + * @return {String} Returns a proof for the key in input as the result. + */ +// Example: +/* +let params = { + index: 1575009568558, + key : "0x77111aaabbbcccdddeeeeffff000002222233333" +} +*/ +const handler = async params => { + const validationError = validate(schema, params); + if (validationError) return validationError; + + const item = await managerDB.getItemByIndex(params.index); + if (!item) { + return error( + `Invalid index. There is no tree with such ${params.index} index.` + ); + } + + // check key value + const { depth } = item; + const maxnumber = BigInt(2 ** depth) - BigInt(1); + + let bN; + try { + bN = BigInt(params.key); + } catch (e) { + return error(`This "${params.key}" key is not a number.`); + } + + if (bN > maxnumber) { + return error( + `This "${params.key}" key is out of range of the tree's depth.` + ); + } + + if (item.config) { + await autoUpdate(params.index); + } + const tree = new SmtLib(item.depth, item.leaves); + const proof = tree.createMerkleProof(params.key); + return proof; +}; + +module.exports = handler; diff --git a/proof-data-provider/methods/utils.js b/proof-data-provider/methods/utils.js new file mode 100644 index 0000000..9842d3a --- /dev/null +++ b/proof-data-provider/methods/utils.js @@ -0,0 +1,33 @@ +const Validator = require("jsonschema").Validator; +const validator = new Validator(); + +const paramsSchema = (name, properties) => { + const paramsSchemaId = `/${name}Params`; + const paramsSchema = { + id: paramsSchemaId, + type: "object", + properties: properties + }; + return paramsSchema; +}; + +const error = (message) => ({ + code: -32602, + message: "Invalid params", + data: message, +}); + +const validate = (schema, params) => { + const res = validator.validate(params, schema); + if (res.valid) return; + + return { + code: -32602, + message: "Invalid params", + data: res.errors.map( + e => `parameter '${e.property.replace("instance.", "")}' ${e.message}` + ) + }; +}; + +module.exports = { validate, error, paramsSchema }; \ No newline at end of file diff --git a/proof-data-provider/package.json b/proof-data-provider/package.json index 0a7461e..c421dc0 100644 --- a/proof-data-provider/package.json +++ b/proof-data-provider/package.json @@ -30,6 +30,7 @@ "ethereumjs-util": "^6.2.0", "jayson": "^3.2.0", "jsbi": "^3.1.1", + "jsonschema": "^1.2.5", "web3": "^1.2.4" }, "devDependencies": { diff --git a/proof-data-provider/server.js b/proof-data-provider/server.js index b0d50e6..656c134 100644 --- a/proof-data-provider/server.js +++ b/proof-data-provider/server.js @@ -3,137 +3,31 @@ const config = require("config"); const jayson = require("jayson/promise"); const { errorHandler, methodHandler } = require("./handler_jayson"); -const server = jayson.server({ - addTreeManually: params => { - const error = errorHandler("ctype1", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("addTreeManually", params); - resolve(result); - }); - }); - }, - - addTreeFromContract: params => { - const error = errorHandler("ctype2", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - // server.error just returns {code: 32602, message: 'Internal error', data: 'specific input error'} - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("addTreeFromContract", params); - resolve(result); - }); - }); - }, - updateTreeManually: params => { - const error = errorHandler("utype1", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("updateTreeManually", params); - resolve(result); - }); - }); - }, +const checkAndCall = (methodName, errorHandlerName, params) => { + const error = errorHandler(errorHandlerName, params); + return error.then(err => { + if (err != null) { + return Promise.reject(server.error(err.code, err.message, err.data)); + } + return Promise.resolve(methodHandler(methodName, params)); + }); +}; - extraUpdateTreeFromContract: params => { - const error = errorHandler("utype2", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("extraUpdateTreeFromContract", params); - resolve(result); - }); - }); - }, - - getProofByKey: params => { - const error = errorHandler("gp1", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("getProofByKey", params); - resolve(result); - }); - }); - }, - getProofByKeys: params => { - const error = errorHandler("gp2", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("getProofByKeys", params); - resolve(result); - }); - }); - }, - getProofByKeyWithCondition: params => { - const error = errorHandler("gp3", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("getProofByKeyWithCondition", params); - resolve(result); - }); - }); - }, - getProofByKeysWithCondition: params => { - const error = errorHandler("gp4", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("getProofByKeysWithCondition", params); - resolve(result); - }); - }); - }, - getRoot: params => { - const error = errorHandler("gR", params); - return error.then(err => { - if (err != null) { - return new Promise((resolve, reject) => { - reject(server.error(err.code, err.message, err.data)); - }); - } - return new Promise(resolve => { - const result = methodHandler("getRoot", params); - resolve(result); - }); - }); - } +const server = jayson.server({ + addTreeManually: params => checkAndCall("addTreeManually", "ctype1", params), + addTreeFromContract: params => + checkAndCall("addTreeFromContract", "ctype2", params), + updateTreeManually: params => + checkAndCall("updateTreeManually", "utype1", params), + extraUpdateTreeFromContract: params => + checkAndCall("extraUpdateTreeFromContract", "utype2", params), + getProofByKey: async params => await require("./methods/getProofByKey")(params), + getProofByKeys: params => checkAndCall("getProofByKeys", "gp2", params), + getProofByKeyWithCondition: params => + checkAndCall("getProofByKeyWithCondition", "gp3", params), + getProofByKeysWithCondition: params => + checkAndCall("getProofByKeysWithCondition", "gp4", params), + getRoot: params => checkAndCall("getRoot", "gR", params) }); server.http().listen(config.get("endpoint.options.port")); diff --git a/proof-data-provider/yarn.lock b/proof-data-provider/yarn.lock index edc9855..e5fbf58 100644 --- a/proof-data-provider/yarn.lock +++ b/proof-data-provider/yarn.lock @@ -2014,6 +2014,11 @@ jsonparse@^1.2.0: resolved "https://registry.yarnpkg.com/jsonparse/-/jsonparse-1.3.1.tgz#3f4dae4a91fac315f71062f8521cc239f1366280" integrity sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA= +jsonschema@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/jsonschema/-/jsonschema-1.2.5.tgz#bab69d97fa28946aec0a56a9cc266d23fe80ae61" + integrity sha512-kVTF+08x25PQ0CjuVc0gRM9EUPb0Fe9Ln/utFOgcdxEIOHuU7ooBk/UPTd7t1M91pP35m0MU1T8M5P7vP1bRRw== + jsprim@^1.2.2: version "1.4.1" resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2"