diff --git a/.gitignore b/.gitignore index 1d04c6e..3a92ee4 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ /package-lock.json /args/config.json /args/param.json +/args/keys.json *test* \ No newline at end of file diff --git a/schema.sql b/args/schema.sql similarity index 100% rename from schema.sql rename to args/schema.sql diff --git a/src/floGlobals.js b/public/floGlobals.js similarity index 91% rename from src/floGlobals.js rename to public/floGlobals.js index 4d9a177..6d7bc6b 100644 --- a/src/floGlobals.js +++ b/public/floGlobals.js @@ -8,6 +8,7 @@ const floGlobals = { FLO: ['https://livenet.flocha.in/', 'https://flosight.duckdns.org/'], FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/'] }, + adminID: "FKAEdnPfjXLHSYwrXQu377ugN4tXU7VGdf", sendAmt: 0.001, fee: 0.0005, tokenURL: "https://ranchimallflo.duckdns.org/", diff --git a/public/fn.js b/public/fn.js index 8b64a9e..25c4b4c 100644 --- a/public/fn.js +++ b/public/fn.js @@ -114,6 +114,16 @@ function getTransactionList() { }); } +function getRate() { + return new Promise((resolve, reject) => { + fetch('/get-rate') + .then(result => responseParse(result, false) + .then(result => resolve(result)) + .catch(error => reject(error))) + .catch(error => reject(error)); + }); +} + function signRequest(request, privKey) { if (typeof request !== "object") throw Error("Request is not an object"); diff --git a/public/home.html b/public/home.html index 5b9c4b4..7a5694e 100644 --- a/public/home.html +++ b/public/home.html @@ -2,6 +2,7 @@ + @@ -9,31 +10,14 @@ - + +
Current FLO Rate:
Login @@ -682,6 +666,14 @@ }).catch(error => console.error(error)) } + function get_rate() { + getRate().then(rate => { + console.log("Rate: ", rate); + let container = document.getElementById("cur-rate"); + container.textContent = "Rs " + parseFloat(rate).toFixed(2); + }).catch(error => console.error(error)) + } + function refresh(init = false) { if (init) console.info("init"); @@ -690,6 +682,7 @@ list_buy(); list_sell(); list_txns(); + get_rate(); if (init || document.getElementById('user-container').style.display === "block") account(); } diff --git a/setup/configure-settings.js b/setup/configure-settings.js new file mode 100644 index 0000000..fa0fb7d --- /dev/null +++ b/setup/configure-settings.js @@ -0,0 +1,120 @@ +const fs = require('fs'); +const getInput = require('./getInput'); + +var config, flag_new; +try { + config = require('./args/config.json'); + flag_new = false; +} catch (error) { + config = { + "secret": null, + "port": "8080", + + "sql_user": null, + "sql_pwd": null, + "sql_db": "exchange", + "sql_host": "localhost" + }; + flag_new = true; +} + +function flaggedYesOrNo(text) { + return new Promise((resolve) => { + if (flag_new) + resolve(true); + else + getInput.YesOrNo(text) + .then(result => resolve(result)) + .catch(error => reject(error)) + }) +} + + +function configurePort() { + return new Promise(resolve => { + getInput.Text('Enter port', config["port"]).then(port => { + config["port"] = port; + resolve(true); + }) + }) +}; + +function configureSQL() { + return new Promise(resolve => { + flaggedYesOrNo('Do you want to re-configure mySQL connection').then(value => { + if (value) { + console.log('Enter mySQL connection values: ') + getInput.Text('Host', config['sql_host']).then(host => { + config['sql_host'] = host; + getInput.Text('Database name', config['sql_db']).then(dbname => { + config['sql_db'] = dbname; + getInput.Text('MySQL username', config['sql_user']).then(sql_user => { + config['sql_user'] = sql_user; + getInput.Text('Mysql password', config['sql_pwd']).then(sql_pwd => { + config['sql_pwd'] = sql_pwd; + resolve(true); + }) + }) + }) + }) + } else + resolve(false); + }) + }) +}; + +function randomizeSessionSecret() { + return new Promise((resolve) => { + flaggedYesOrNo('Do you want to randomize the session secret').then(value => { + if (value) { + let N = Math.floor(Math.random() * (64 - 32 + 1)) + 32; + var secret = ''; + var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (var i = 0; i < N; i++) + secret += characters.charAt(Math.floor(Math.random() * characters.length)); + config['secret'] = secret + resolve(true); + } else + resolve(false); + }) + }) +} + +function configure() { + return new Promise((resolve, reject) => { + configurePort().then(port_result => { + randomizeSessionSecret().then(secret_result => { + configureSQL().then(sql_result => { + fs.writeFile(__dirname + '/../args/config.json', JSON.stringify(config), 'utf8', (err) => { + if (err) { + console.error(err); + return reject(false); + } + console.log('Configuration successful!'); + if (sql_result) { + getInput.YesOrNo('Do you want to create schema in the database').then(value => { + if (value) { + const createSchema = require('./create-schema'); + createSchema().then(result => resolve(result)) + .catch(error => { + console.log('Retry using: \n' + 'npm run create-schema'); + reject(error); + }); + } else { + console.log('To create schema, use: \n' + 'npm run create-schema'); + resolve(true); + } + }); + } else + resolve(true); + }) + }) + }) + }); + }) +} + +if (!module.parent) + configure().then(_ => null).catch(_ => null); +else + module.exports = configure; \ No newline at end of file diff --git a/setup/create-schema.js b/setup/create-schema.js new file mode 100644 index 0000000..36a2259 --- /dev/null +++ b/setup/create-schema.js @@ -0,0 +1,37 @@ +const fs = require('fs'); +const config = require('./args/config.json'); +let Database = require('./src/database'); + +function createSchema() { + return new Promise((resolve, reject) => { + fs.readFile(__dirname + '/../args/schema.sql', 'utf8', (err, data) => { + if (err) { + console.error(err); + return reject(null); + } + Database(config["sql_user"], config["sql_pwd"], config["sql_db"], config["sql_host"]).then(DB => { + let txQueries = data.split(';'); + txQueries.pop(); + txQueries = txQueries.map(q => q.trim().replace(/\n/g, ' ')); + console.log(txQueries); + DB.transaction(txQueries).then(_ => { + console.log('SQL Schema created successfully!'); + resolve(true); + }).catch(error => { + console.error(error.message); + console.log('SQL Schema creation failed! Check user permission'); + reject(true); + }); + }).catch(error => { + console.error(error); + console.log('Unable to connect to MySQL database! Check user permission'); + reject(false); + }); + }); + }); +} + +if (!module.parent) + createSchema().then(_ => null).catch(_ => null); +else + module.exports = createSchema; \ No newline at end of file diff --git a/setup/getInput.js b/setup/getInput.js new file mode 100644 index 0000000..c0fc277 --- /dev/null +++ b/setup/getInput.js @@ -0,0 +1,42 @@ +const readline = require('readline'); + +const getInput = { + Text: function(text, current = null) { + return new Promise((resolve) => { + let r = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + r.question(`${text} :` + (current ? `(${current})` : ''), value => { + r.close(); + value = value || current; + if (value === null) { + console.log("Please enter a value!"); + this.Text(text, current).then(result => resolve(result)); + } else + resolve(value); + }); + }) + }, + + YesOrNo: function(text, def_value = "YES") { + return new Promise((resolve) => { + let r = readline.createInterface({ + input: process.stdin, + output: process.stdout + }); + r.question(`${text}? [YES/NO] : (${def_value})`, value => { + r.close(); + value = (value || def_value).toLowerCase(); + value = ['yes', 'y'].includes(value) ? true : ['no', 'n'].includes(value) ? false : null; + if (value === null) { + console.log("Please enter a valid value!"); + this.YesOrNo(text, def_value).then(result => resolve(result)); + } else + resolve(value); + }); + }) + } +} + +module.exports = getInput; \ No newline at end of file diff --git a/setup/help.js b/setup/help.js new file mode 100644 index 0000000..9f2d8b1 --- /dev/null +++ b/setup/help.js @@ -0,0 +1,11 @@ +let message = ` +Exchange market + +npm install - Install the app and node modules. +npm run setup - Finish the setup (configure and reset password). +npm run configure - Configure the app. +npm run reset-password - Reset the password (for private-key). +npm run create-schema - Create the schema in MySQL database. +npm run help - List all commands. +npm start - Start the application. +`; \ No newline at end of file diff --git a/setup/post-install.js b/setup/post-install.js new file mode 100644 index 0000000..e36ffef --- /dev/null +++ b/setup/post-install.js @@ -0,0 +1,33 @@ +const getInput = require("./getInput"); + +let message = ` +Exchange Market is installed +To list all commands, use: +npm run help +`; +console.log(message); + +getInput.YesOrNo('Do you want to finish the setup now').then(value => { + if (value) { + let configureSettings = require('./configure-settings'); + configureSettings() + .then(_ => console.log('To Re-configure, use:')) + .catch(_ => console.log('Finish the configuration later using: ')) + .finally(_ => { + console.log('npm run configure'); + getInput.YesOrNo('Do you want to Reset password for private key now').then(value => { + if (value) { + let resetPassword = require('./reset-password'); + resetPassword() + .then(_ => console.log('To reset the password again, use: ')) + .catch(_ => console.log('Reset the password later using: ')) + .finally(_ => { + console.log('npm run reset-password'); + }) + } else + console.log('Reset the password later using:\n' + 'npm run reset-password'); + }) + }) + } else + console.log('Finish the setup later using:\n' + 'npm run setup'); +}) \ No newline at end of file diff --git a/setup/reset-password.js b/setup/reset-password.js new file mode 100644 index 0000000..be76064 --- /dev/null +++ b/setup/reset-password.js @@ -0,0 +1,83 @@ +const fs = require('fs'); +const getInput = require('./getInput'); + +const floGlobals = require('../public/floGlobals'); +require('../src/set_globals'); +require('../src/lib'); +require('../src/floCrypto'); + +console.log(__dirname); + +function validateKey(privKey) { + return new Promise((resolve, reject) => { + if (floCrypto.verifyPrivKey(privKey, floGlobals.adminID)) + return resolve(privKey); + else { + getInput.Text('Incorrect Private Key! Re-Enter: (Cancel)', 'Cancel').then(value => { + if (value === 'Cancel') + return reject(true); + validateKey(value) + .then(result => resolve(result)) + .catch(error => reject(error)) + }); + } + }) +} + +function getPassword() { + return new Promise((resolve, reject) => { + getInput.Text(`Enter a password [Minimum 8 characters]`, 'Cancel').then(value1 => { + if (value1 === 'Cancel') + return reject(true); + else if (value1.length < 8) { + console.log('Password length must be minimum of 8 characters'); + getPassword() + .then(result => resolve(result)) + .catch(error => reject(error)) + } else { + getInput.Text(`Re-enter password`).then(value2 => { + if (value1 !== value2) { + console.log('Passwords doesnot match! Try again.'); + getPassword() + .then(result => resolve(result)) + .catch(error => reject(error)) + } else + resolve(value1); + }) + } + }); + }) +} + +function resetPassword() { + return new Promise((resolve, reject) => { + getInput.Text(`Enter private key for adminID (${floGlobals.adminID})`).then(value => { + validateKey(value).then(privKey => { + getPassword().then(password => { + let encrypted = Crypto.AES.encrypt(privKey, password); + let randNum = floCrypto.randInt(10, 15); + let splitShares = floCrypto.createShamirsSecretShares(encrypted, randNum, randNum); + fs.writeFile(__dirname + '/../args/keys.json', JSON.stringify(splitShares), 'utf8', (err) => { + if (err) { + console.error(err); + return reject(false); + } + console.log('Password reset successful!'); + resolve(true); + }) + }).catch(error => { + console.log('Password reset cancelled!'); + reject(true); + }) + }).catch(error => { + console.log('Password reset cancelled!'); + reject(true); + }) + }) + }) +} + +if (!module.parent) + resetPassword().then(_ => null).catch(error => console.error(error)); +else + module.exports = resetPassword; \ No newline at end of file diff --git a/src/app.js b/src/app.js index f491ffd..57fe489 100644 --- a/src/app.js +++ b/src/app.js @@ -50,8 +50,9 @@ module.exports = function App(secret, DB) { app.get('/list-sellorders', Request.ListSellOrders); app.get('/list-buyorders', Request.ListBuyOrders); - //list all process transactions + //list all process transactions and rate app.get('/list-transactions', Request.ListTransactions); + app.get('/get-rate', Request.getRate) //get account details app.get('/account', Request.Account); diff --git a/src/database.js b/src/database.js index e82ab14..d5b35fd 100644 --- a/src/database.js +++ b/src/database.js @@ -66,6 +66,8 @@ function Database(user, password, dbname, host = 'localhost') { queryFn(result); } }; + if (!Array.isArray(q_i)) + q_i = [q_i]; if (q_i[1]) conn.query(q_i[0], q_i[1], callback); else diff --git a/src/main.js b/src/main.js index 54e5916..559d7d0 100644 --- a/src/main.js +++ b/src/main.js @@ -1,5 +1,5 @@ const config = require('../args/config.json'); -global.floGlobals = require("./floGlobals"); +global.floGlobals = require('../public/floGlobals'); require('./set_globals'); require('./lib'); require('./floCrypto'); @@ -7,22 +7,31 @@ require('./floBlockchainAPI'); const Database = require("./database"); const App = require('./app'); +const floGlobals = require('../public/floGlobals'); const PORT = config['port']; module.exports = function startServer(public_dir) { - - global.myPrivKey = config["blockchain_private"]; - global.myPubKey = floCrypto.getPubKeyHex(global.myPrivKey); - global.myFloID = floCrypto.getFloID(global.myPubKey); - - if (global.myFloID !== config["blockchain_id"] || !floCrypto.verifyPrivKey(global.myPrivKey, global.myFloID)) { - console.error("Invalid Private Key for adminID"); - return; + try { + var _tmp = require('../args/keys.json'); + _tmp = floCrypto.retrieveShamirSecret(_tmp); + var _pass = process.env.password; + _tmp = Crypto.AES.decrypt(_tmp, _pass); + if (floCrypto.verifyPrivKey(_tmp, floGlobals.adminID)) { + global.myPrivKey = _tmp; + global.myPubKey = floCrypto.getPubKeyHex(global.myPrivKey); + global.myFloID = floCrypto.getFloID(global.myPubKey); + } else { + console.error('Loaded wrong private key!'); + process.exit(1); + } + } catch (error) { + console.error('Unable to load private key!'); + process.exit(1); } - floGlobals.adminID = config["blockchain_id"]; + global.PUBLIC_DIR = public_dir; - console.log(PUBLIC_DIR, global.myFloID); - + console.debug(PUBLIC_DIR, global.myFloID); + Database(config["sql_user"], config["sql_pwd"], config["sql_db"], config["sql_host"]).then(DB => { const app = App(config['secret'], DB); app.listen(PORT, () => console.log(`Server Running at port ${PORT}`)); diff --git a/src/market.js b/src/market.js index f85bdc6..e25a29b 100644 --- a/src/market.js +++ b/src/market.js @@ -52,8 +52,6 @@ const tokenAPI = { } } - - function getRates() { return new Promise((resolve, reject) => { getRates.FLO_USD().then(FLO_rate => { @@ -92,6 +90,10 @@ getRates.USD_INR = function() { }); } +function returnRates() { + return net_FLO_price; +} + function addSellOrder(floID, quantity, min_price) { return new Promise((resolve, reject) => { if (!floID || !floCrypto.validateAddr(floID)) @@ -657,6 +659,7 @@ function transactionReCheck() { } module.exports = { + returnRates, addBuyOrder, addSellOrder, cancelOrder, diff --git a/src/request.js b/src/request.js index 9dc2b1f..56568c3 100644 --- a/src/request.js +++ b/src/request.js @@ -243,6 +243,11 @@ function ListTransactions(req, res) { .catch(error => res.status(INTERNAL.e_code).send("Try again later!")); } +function getRate(req, res) { + let rate = market.returnRates(); + res.send(`${rate}`); +} + function Account(req, res) { const setLogin = function(message) { let randID = floCrypto.randString(16, true); @@ -409,6 +414,7 @@ module.exports = { ListSellOrders, ListBuyOrders, ListTransactions, + getRate, Account, DepositFLO, WithdrawFLO,