From c5b43291ab9719b088e41096247da27c7a73e55d Mon Sep 17 00:00:00 2001 From: sairajzero Date: Tue, 15 Nov 2022 05:14:25 +0530 Subject: [PATCH] API(admin): generate, reshare and discard sinks - generateSink: generates a sinkID for given group - reshareSink: regenerate the shares for given id and distribute - discardSink: discards the given sink id Note: All these APIs are admin-only --- docs/scripts/floExchangeAPI.js | 96 +++++++++++++++++++++++++++++++++- src/app.js | 5 ++ src/backup/head.js | 69 ++++++++++++++++++++++-- src/keys.js | 22 +++++++- src/request.js | 47 ++++++++++++++++- 5 files changed, 231 insertions(+), 8 deletions(-) diff --git a/docs/scripts/floExchangeAPI.js b/docs/scripts/floExchangeAPI.js index 4057c88..5730ba1 100644 --- a/docs/scripts/floExchangeAPI.js +++ b/docs/scripts/floExchangeAPI.js @@ -519,6 +519,7 @@ DUPLICATE_SIGNATURE: '017', SESSION_INVALID: '018', SESSION_EXPIRED: '019', + INVALID_VALUE: '020', INVALID_TOKEN_NAME: '021', INVALID_NUMBER: '022', INVALID_TYPE: '023', @@ -610,7 +611,7 @@ exchangeAPI.getSink = function (service = serviceList.EXCHANGE) { return new Promise((resolve, reject) => { if (!(service in serviceList)) - return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, 'service required', errorCode.INVALID_TYPE)); + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, 'service required', errorCode.INVALID_VALUE)); fetch_api('/get-sink?service=' + service) .then(result => { responseParse(result, false) @@ -1557,6 +1558,99 @@ }) } + exchangeAPI.generateSink = function (group, floID, privKey) { + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); + if (floID !== DEFAULT.marketID) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Access Denied", errorCode.ACCESS_DENIED)); + let request = { + floID: floID, + group: group, + timestamp: Date.now() + }; + request.pubKey = floCrypto.getPubKeyHex(privKey); + request.sign = signRequest({ + type: "generate_sink", + group: group, + timestamp: request.timestamp + }, privKey); + console.debug(request); + + fetch_api('/generate-sink', { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(request) + }).then(result => { + responseParse(result, false) + .then(result => resolve(result)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + } + + exchangeAPI.reshareSink = function (sinkID, floID, privKey) { + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); + if (floID !== DEFAULT.marketID) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Access Denied", errorCode.ACCESS_DENIED)); + let request = { + floID: floID, + id: sinkID, + timestamp: Date.now() + }; + request.pubKey = floCrypto.getPubKeyHex(privKey); + request.sign = signRequest({ + type: "reshare_sink", + id: sinkID, + timestamp: request.timestamp + }, privKey); + console.debug(request); + + fetch_api('/reshare-sink', { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(request) + }).then(result => { + responseParse(result, false) + .then(result => resolve(result)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + } + + exchangeAPI.discardSink = function (sinkID, floID, privKey) { + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Invalid Private Key", errorCode.INVALID_PRIVATE_KEY)); + if (floID !== DEFAULT.marketID) + return reject(ExchangeError(ExchangeError.BAD_REQUEST_CODE, "Access Denied", errorCode.ACCESS_DENIED)); + let request = { + floID: floID, + id: sinkID, + timestamp: Date.now() + }; + request.pubKey = floCrypto.getPubKeyHex(privKey); + request.sign = signRequest({ + type: "discard_sink", + id: sinkID, + timestamp: request.timestamp + }, privKey); + console.debug(request); + + fetch_api('/discard-sink', { + method: "POST", + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(request) + }).then(result => { + responseParse(result, false) + .then(result => resolve(result)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + } + exchangeAPI.init = function refreshDataFromBlockchain() { return new Promise((resolve, reject) => { let nodes, assets, tags, lastTx; diff --git a/src/app.js b/src/app.js index 55ed7ec..7b6b948 100644 --- a/src/app.js +++ b/src/app.js @@ -88,6 +88,11 @@ module.exports = function App(secret) { app.post('/withdraw-token', Request.WithdrawToken); app.post('/get-transact', Request.GetUserTransacts); + //generate or discard sinks (admin only) + app.post('/generate-sink', Request.GenerateSink); + app.post('/reshare-sink', Request.DiscardSink); + app.post('/discard-sink', Request.DiscardSink); + //convert from or to coin app.get('/get-convert-values', Request.GetConvertValues); app.post('/convert-to', Request.ConvertTo); diff --git a/src/backup/head.js b/src/backup/head.js index 5ec9b8e..ddfcd4d 100644 --- a/src/backup/head.js +++ b/src/backup/head.js @@ -218,11 +218,8 @@ function informLiveNodes(init) { } else if (!flag) { console.log("Starting the exchange..."); //generate a sinkID for each group in starting list - keys.sink_groups.generate_list.forEach(group => { - let newSink = floCrypto.generateNewID(); - console.debug("Generated sink:", group, newSink.floID); - sendSharesToNodes(newSink.floID, group, generateShares(newSink.privKey)); - }) + keys.sink_groups.initial_list.forEach(group => + generateSink(group).then(_ => null).catch(e => console.error(e))); } }).catch(error => console.error(error)); }); @@ -276,6 +273,63 @@ function slaveConnect(floID, pubKey, ws, sinks) { } } +const eCode = require('../docs/scripts/floExchangeAPI').errorCode; + +function generateSink(group) { + return new Promise((resolve, reject) => { + if (!keys.sink_groups.generate_list.includes(group)) + return reject(INVALID(eCode.INVALID_VALUE, `Invalid Group ${group}`)); + try { + let newSink = floCrypto.generateNewID(); + console.debug("Generated sink:", group, newSink.floID); + sendSharesToNodes(newSink.floID, group, generateShares(newSink.privKey)); + resolve(`Generated ${newSink.floID} (${group})`); + } catch (error) { + reject(error) + } + }) +} + +function reshareSink(id) { + return new Promise((resolve, reject) => { + if (!floCrypto.validateAddr(data.id)) + return reject(INVALID(eCode.INVALID_VALUE, `Invalid ID ${id}`)); + else { + let group = keys.sink_chest.find_group(id); + if (!group) + return reject(INVALID(eCode.NOT_FOUND, `ID ${id} not found`)); + else keys.checkIfDiscarded(id).then(result => { + if (result) + return reject(INVALID(eCode.NOT_FOUND, `ID is discarded`)); + try { + reconstructShares(group, id); + resolve(`Resharing ${id} (${group})`); + } catch (error) { + reject(error); + } + }).catch(error => reject(error)) + } + + }) +} + +function discardSink(id) { + return new Promise((resolve, reject) => { + if (!floCrypto.validateAddr(data.id)) + return reject(INVALID(eCode.INVALID_VALUE, `Invalid ID ${id}`)); + else if (!keys.sink_chest.find_group(id)) + return reject(INVALID(eCode.NOT_FOUND, `ID ${id} not found`)); + else keys.checkIfDiscarded(id).then(result => { + if (result) + return reject(INVALID(eCode.DUPLICATE_ENTRY, `ID already discarded`)); + keys.discardSink(id).then(result => { + console.debug("Discarded sink:", id); + resolve(result); + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) +} + function checkForDiscardedSinks() { let cur_time = Date.now(), all_sinks = keys.sink_chest.get_all(); @@ -401,6 +455,11 @@ function initProcess(app) { module.exports = { init: initProcess, collectAndCall, + sink: { + generate: generateSink, + reshare: reshareSink, + discard: discardSink + }, set nodeList(list) { nodeURL = list; nodeKBucket = new K_Bucket(floGlobals.adminID, Object.keys(nodeURL)); diff --git a/src/keys.js b/src/keys.js index db599e5..1a90175 100644 --- a/src/keys.js +++ b/src/keys.js @@ -289,14 +289,25 @@ function checkIfDiscarded(id) { }) } +function discardSink(id) { + return new Promise((resolve, reject) => { + DB.query("INSERT INTO discardedSinks(floID) VALUE (?)", [id]) + .then(result => resolve(`Discarded ${id}`)) + .catch(error => reject(error)) + }) +} + //Sink groups and chest const sink_groups = { get EXCHANGE() { return "exchange" }, get CONVERT() { return "convert" }, get BLOCKCHAIN_BONDS() { return "blockchain_bonds" }, get BOBS_FUND() { return "bobs_fund" }, - get generate_list() { //list to generate when starting exchange + get initial_list() { //list to generate when starting exchange return [this.EXCHANGE, this.CONVERT] + }, + get generate_list() { //list allowed to generate + return [this.EXCHANGE, this.CONVERT, this.BLOCKCHAIN_BONDS, this.BOBS_FUND] } }; @@ -343,6 +354,14 @@ const sink_ids = {}, sink_chest = { i = floCrypto.randInt(0, ids.length); return ids[i]; }, + find_group(id) { + let group = null; + for (let g in sink_ids) + if (id in sink_ids[g]) { + group = g; break; + } + return group; + }, get_all() { let ids = {}; for (let g in sink_ids) @@ -359,6 +378,7 @@ module.exports = { getStoredList, getDiscardedList, checkIfDiscarded, + discardSink, set node_priv(key) { _.node_priv = key; }, diff --git a/src/request.js b/src/request.js index c08a19e..9b72d7c 100644 --- a/src/request.js +++ b/src/request.js @@ -6,6 +6,7 @@ const conversion = require('./services/conversion'); const blockchain_bonds = require("./services/bonds"); const bobs_fund = require("./services/bobs-fund"); const background = require("./background"); +const sink = require("./backup/head").sink; const keys = require("./keys"); const { @@ -307,6 +308,47 @@ function RemoveDistributor(req, res) { }, () => market.removeDistributor(data.distributor, data.asset)); } +function GenerateSink(req, res) { + let data = req.body; + if (data.floID !== floGlobals.adminID) + res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); + else if (!data.pubKey) + res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); + else processRequest(res, data.floID, data.pubKey, data.sign, "Generate Sink", { + type: "generate_sink", + group: data.group, + timestamp: data.timestamp + }, () => sink.generate(group)); +} + +function ReshareSink(req, res) { + let data = req.body; + if (data.floID !== floGlobals.adminID) + res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); + else if (!data.pubKey) + res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); + else if (!floCrypto.validateAddr(data.id)) + res.status(INVALID.e_code).send(INVALID.str(eCode.INVALID_VALUE, `Invalid ID ${data.id}`)); + else processRequest(res, data.floID, data.pubKey, data.sign, "Reshare Sink", { + type: "reshare_sink", + id: data.id, + timestamp: data.timestamp + }, () => sink.reshare(id)); +} + +function DiscardSink(req, res) { + let data = req.body; + if (data.floID !== floGlobals.adminID) + res.status(INVALID.e_code).send(INVALID.str(eCode.ACCESS_DENIED, "Access Denied")); + else if (!data.pubKey) + res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Public key missing")); + else processRequest(res, data.floID, data.pubKey, data.sign, "Discard Sink", { + type: "discard_sink", + id: data.id, + timestamp: data.timestamp + }, () => sink.discard(id)); +} + function ConvertTo(req, res) { let data = req.body; processRequest(res, data.floID, data.pubKey, data.sign, "Conversion", { @@ -518,7 +560,7 @@ function GetSink(req, res) { if (!service) res.status(INVALID.e_code).send(INVALID.str(eCode.MISSING_PARAMETER, "Missing service parameter")); else if (!(service in serviceList)) - res.status(INVALID.e_code).send(INVALID.str(eCode.INVALID_TYPE, "Invalid service parameter")); + res.status(INVALID.e_code).send(INVALID.str(eCode.INVALID_VALUE, "Invalid service parameter")); else { let group; switch (service) { @@ -640,6 +682,9 @@ module.exports = { RemoveUserTag, AddDistributor, RemoveDistributor, + GenerateSink, + ReshareSink, + DiscardSink, GetConvertValues, ConvertTo, ConvertFrom,