From 79ed3ad33086972d0041c924dc4a1505443d9a9f Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 27 Jan 2023 18:03:17 +0530 Subject: [PATCH 1/6] Adding websocket api calls The following api now have ws support for large data transmission - /addr/:addr/ - /addrs/:addrs/txs --- lib/addresses.js | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/common.js | 23 ++++++++++++++ lib/index.js | 7 ++++- 3 files changed, 111 insertions(+), 1 deletion(-) diff --git a/lib/addresses.js b/lib/addresses.js index a855a5f..dca175f 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -40,6 +40,40 @@ AddressController.prototype.show = function(req, res) { }); }; +AddressController.prototype.show_ws = function(req, ws) { + var self = this; + /* + var options = { + noTxList: parseInt(req.query.noTxList) + }; + */ + + if (req.query.from && req.query.to) { + options.from = parseInt(req.query.from); + options.to = parseInt(req.query.to); + } + + self._address.getAddressSummary(req.addr, options, function (err, data) { + if(err) { + return self.common.handleErrors_ws(err, ws); + } + + ws.send({data}); + + }, function(err, result) { + + if(err) { + return self.common.handleErrors_ws(err, ws); + } + + if(ws.readyState === ws.OPEN){ + ws.send({result}); + ws.close(); + } + + }); +}; + AddressController.prototype.balance = function(req, res) { this.addressSummarySubQuery(req, res, 'balanceSat'); }; @@ -278,6 +312,54 @@ AddressController.prototype.multitxs = function(req, res) { }); }; +AddressController.prototype.multitxs_ws = function(req, ws) { + var self = this; + + var options = { + from: parseInt(req.query.from) || parseInt(req.body.from) || 0 + }; + + options.to = parseInt(req.query.to) || parseInt(req.body.to) || parseInt(options.from) + 10; + + options.txNotNeeded = true; + + var transformOptions = self._getTransformOptions(req); + + self._address.getAddressHistory(req.addrs, options, function (err, data) { + if(err) { + return self.common.handleErrors_ws(err, ws, false); + } + + self.txController.transformTransaction(tx, transformOptions, function(err, data){ + + if(err) { + return self.common.handleErrors_ws(err, ws, false); + } + + ws.send({data}) + + }); + + }, function(err, result) { + + if(err) { + return self.common.handleErrors_ws(err, ws); + } + + var ret = { + totalItems: result.totalCount, + from: options.from, + to: Math.min(options.to, result.totalCount) + } + + if(ws.readyState === ws.OPEN){ + ws.send({result: ret}); + ws.close(); + } + + }); +}; + AddressController.prototype.transformAddressHistoryForMultiTxs = function(txs, options, callback) { var self = this; diff --git a/lib/common.js b/lib/common.js index d11ad79..844c724 100644 --- a/lib/common.js +++ b/lib/common.js @@ -13,6 +13,13 @@ Common.prototype.notReady = function (err, res, p) { res.status(503).send('Server not yet ready. Sync Percentage:' + p); }; +Common.prototype.notReady_ws = function (err, ws, p) { + if(ws.readyState !== ws.OPEN) + return; + ws.send({error: {message: 'Server not yet ready. Sync Percentage:' + p, code: 503}}); + ws.close(); +}; + Common.prototype.handleErrors = function (err, res) { if (err) { if (err.code) { @@ -29,6 +36,22 @@ Common.prototype.handleErrors = function (err, res) { } }; +Common.prototype.handleErrors_ws = function (err, ws, close = true) { + if(ws.readyState !== ws.OPEN) + return; + if (err) { + if (err.code) + ws.send({error: {message: err.message, code: err.code}}); + else { + this.log.error(err.stack); + ws.send({error: {message: err.message, code: 503}}); + } + } else { + ws.send({error: {message: 'Not found', code: 404}}); + } + if(close) + ws.close(); +} Common.prototype.translateInputAddresses= function(addresses) { var self = this; diff --git a/lib/index.js b/lib/index.js index b65f649..b887c2c 100644 --- a/lib/index.js +++ b/lib/index.js @@ -143,7 +143,7 @@ FlosightAPI.prototype._getRateLimiter = function() { return limiter; }; -FlosightAPI.prototype.setupRoutes = function(app) { +FlosightAPI.prototype.setupRoutes = function(app, express, express_ws) { var self = this; @@ -184,6 +184,9 @@ FlosightAPI.prototype.setupRoutes = function(app) { } }); + //Add ws listener to app + var expressWS = express_ws(app); + //Block routes var blockOptions = { node: this.node, @@ -216,10 +219,12 @@ FlosightAPI.prototype.setupRoutes = function(app) { // Address routes var addresses = new AddressController(this.node, this.translateAddresses); app.get('/addr/:addr', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.show.bind(addresses)); + app.ws('/addr/:addr', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.show_ws.bind(addresses)); app.get('/addr/:addr/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.utxo.bind(addresses)); app.get('/addrs/:addrs/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses)); app.post('/addrs/utxo', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multiutxo.bind(addresses)); app.get('/addrs/:addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses)); + app.ws('/addrs/:addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs_ws.bind(addresses)); app.post('/addrs/txs', this.cacheShort(), addresses.checkAddrs.bind(addresses), addresses.multitxs.bind(addresses)); // Address property routes From ad9fe0c80a06c9368128e320c7001ec1de268f74 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 27 Jan 2023 18:21:04 +0530 Subject: [PATCH 2/6] Fixed: sub-query APIs value conversion - Fixed: Sat-to-Coin conversion applied over Coin value - Fixed: totalReceived and totalSent APIs returning Sat instead of Coin value --- lib/addresses.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/addresses.js b/lib/addresses.js index dca175f..3fdba58 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -27,7 +27,7 @@ AddressController.prototype.show = function(req, res) { options.to = parseInt(req.query.to); } - this._address.getAddressSummary(req.addr, options, function(err, data) { + self._address.getAddressSummary(req.addr, options, function(err, data) { if(err) { return self.common.handleErrors(err, res); } @@ -75,24 +75,24 @@ AddressController.prototype.show_ws = function(req, ws) { }; AddressController.prototype.balance = function(req, res) { - this.addressSummarySubQuery(req, res, 'balanceSat'); + this.addressSummarySubQuery(req, res, 'balance'); }; AddressController.prototype.totalReceived = function(req, res) { - this.addressSummarySubQuery(req, res, 'totalReceivedSat'); + this.addressSummarySubQuery(req, res, 'totalReceived'); }; AddressController.prototype.totalSent = function(req, res) { - this.addressSummarySubQuery(req, res, 'totalSentSat'); + this.addressSummarySubQuery(req, res, 'totalSent'); }; AddressController.prototype.unconfirmedBalance = function(req, res) { - this.addressSummarySubQuery(req, res, 'unconfirmedBalanceSat'); + this.addressSummarySubQuery(req, res, 'unconfirmedBalance'); }; AddressController.prototype.addressSummarySubQuery = function(req, res, param) { var self = this; - this.getAddressSummary(req.addr, {}, function(err, data) { + self.getAddressSummary(req.addr, {}, function(err, data) { if(err) { return self.common.handleErrors(err, res); } @@ -104,21 +104,21 @@ AddressController.prototype.addressSummarySubQuery = function(req, res, param) { AddressController.prototype.getAddressSummary = function(address, options, callback) { var self = this; - this._address.getAddressSummary(address, options, function(err, summary) { + self._address.getAddressSummary(address, options, function(err, summary) { if(err) { return callback(err); } var transformed = { address: self.common.translateOutputAddress(address), - balance: Unit.fromSatoshis(summary.balance).toBTC(), - balanceSat: summary.balance, - totalReceived: Unit.fromSatoshis(summary.totalReceived).toBTC(), + balance: summary.balance, + balanceSat: summary.balanceSat, + totalReceived: summary.totalReceived, totalReceivedSat: summary.totalReceivedSat, - totalSent: Unit.fromSatoshis(summary.totalSent).toBTC(), + totalSent: summary.totalSent, totalSentSat: summary.totalSentSat, - unconfirmedBalance: Unit.fromSatoshis(summary.unconfirmedBalance).toBTC(), - unconfirmedBalanceSat: summary.unconfirmedBalance, + unconfirmedBalance: summary.unconfirmedBalance, + unconfirmedBalanceSat: summary.unconfirmedBalanceSat, unconfirmedTxApperances: summary.unconfirmedAppearances, // misspelling - ew txApperances: summary.txApperances, // yuck transactions: summary.transactions @@ -148,7 +148,7 @@ AddressController.prototype.checkAddrs = function(req, res, next) { } if(!_.isArray(req.addrs) || _.compact(req.addrs).length < 1) { - return this.common.handleErrors({ + return self.common.handleErrors({ message: 'Must include address', code: 1 }, res); @@ -159,7 +159,7 @@ AddressController.prototype.checkAddrs = function(req, res, next) { req.addr = req.addrs[0]; } catch(e) { console.log('[addresses.js.130]', e); //TODO - return this.common.handleErrors({ + return self.common.handleErrors({ message: 'Invalid address: ' + e, code: 1 }, res); @@ -171,7 +171,7 @@ console.log('[addresses.js.130]', e); //TODO AddressController.prototype.utxo = function(req, res) { var self = this; - this._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) { + self._address.getAddressUnspentOutputs(req.addr, {}, function(err, utxos) { var results; if(err) { return self.common.handleErrors(err, res); From f4029eaf8868fdbeeba3af36eed1a86ce1e0c146 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 27 Jan 2023 22:20:43 +0530 Subject: [PATCH 3/6] Update package.json --- package.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index bba7855..3fd5c14 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "flosight-api", "description": "A Florincoin blockchain REST and web socket API service for Flocore Node.", - "version": "5.1.0-beta.75", - "repository": "git://github.com/oipwg/flosight-api.git", + "version": "5.1.1-beta-rm", + "repository": "git://github.com/ranchimall/flosight-api.git", "bugs": { - "url": "https://github.com/oipwg/flosight-api/issues" + "url": "https://github.com/ranchimall/flosight-api/issues" }, - "homepage": "https://github.com/oipwg/flosight-api", + "homepage": "https://github.com/ranchimall/flosight-api", "license": "MIT", "keywords": [ "flosight", From dbb8cafdc32bc8536253ff1b1c92ac0e52f95b95 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 4 Feb 2023 17:53:50 +0530 Subject: [PATCH 4/6] Minor fixes and binding stop-flag to on-close - Set flag_stop when ws or http conn is closed - Fixed: incorrect order of parameters to ws fns. ie, (ws, req) is the correct order - Pass noTxList flag to options in show_ws and addressSummarySubQuery - Fixed: minor typos/misspellings --- lib/addresses.js | 27 +++++++++++++++++---------- lib/common.js | 4 ++++ 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/addresses.js b/lib/addresses.js index 3fdba58..1e2ef84 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -22,6 +22,8 @@ AddressController.prototype.show = function(req, res) { noTxList: parseInt(req.query.noTxList) }; + self.common.bindStopFlagOnClose(res, options); + if (req.query.from && req.query.to) { options.from = parseInt(req.query.from); options.to = parseInt(req.query.to); @@ -40,19 +42,17 @@ AddressController.prototype.show = function(req, res) { }); }; -AddressController.prototype.show_ws = function(req, ws) { +AddressController.prototype.show_ws = function(ws, req) { var self = this; - /* - var options = { - noTxList: parseInt(req.query.noTxList) - }; - */ + var options = { noTxList: true }; if (req.query.from && req.query.to) { options.from = parseInt(req.query.from); options.to = parseInt(req.query.to); } + self.common.bindStopFlagOnClose(ws, options); + self._address.getAddressSummary(req.addr, options, function (err, data) { if(err) { return self.common.handleErrors_ws(err, ws); @@ -92,7 +92,10 @@ AddressController.prototype.unconfirmedBalance = function(req, res) { AddressController.prototype.addressSummarySubQuery = function(req, res, param) { var self = this; - self.getAddressSummary(req.addr, {}, function(err, data) { + var options = { noTxList: true }; + self.common.bindStopFlagOnClose(res, options); + + self.getAddressSummary(req.addr, options, function(err, data) { if(err) { return self.common.handleErrors(err, res); } @@ -119,8 +122,8 @@ AddressController.prototype.getAddressSummary = function(address, options, callb totalSentSat: summary.totalSentSat, unconfirmedBalance: summary.unconfirmedBalance, unconfirmedBalanceSat: summary.unconfirmedBalanceSat, - unconfirmedTxApperances: summary.unconfirmedAppearances, // misspelling - ew - txApperances: summary.txApperances, // yuck + unconfirmedTxApperances: summary.unconfirmedTxApperances, + txApperances: summary.txApperances, transactions: summary.transactions }; @@ -285,6 +288,8 @@ AddressController.prototype.multitxs = function(req, res) { options.to = parseInt(req.query.to) || parseInt(req.body.to) || parseInt(options.from) + 10; + self.common.bindStopFlagOnClose(res, options); + self._address.getAddressHistory(req.addrs, options, function(err, result) { if(err) { @@ -312,7 +317,7 @@ AddressController.prototype.multitxs = function(req, res) { }); }; -AddressController.prototype.multitxs_ws = function(req, ws) { +AddressController.prototype.multitxs_ws = function(ws, req) { var self = this; var options = { @@ -325,6 +330,8 @@ AddressController.prototype.multitxs_ws = function(req, ws) { var transformOptions = self._getTransformOptions(req); + self.common.bindStopFlagOnClose(ws, options); + self._address.getAddressHistory(req.addrs, options, function (err, data) { if(err) { return self.common.handleErrors_ws(err, ws, false); diff --git a/lib/common.js b/lib/common.js index 844c724..cd6ff2d 100644 --- a/lib/common.js +++ b/lib/common.js @@ -20,6 +20,10 @@ Common.prototype.notReady_ws = function (err, ws, p) { ws.close(); }; +Common.prototype.bindStopFlagOnClose = function (res_ws, obj) { + res_ws.on("close", () => obj.flag_stop = true); +}; + Common.prototype.handleErrors = function (err, res) { if (err) { if (err.code) { From 20eb50b281b7f458389041089e74def638e208f1 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sun, 5 Feb 2023 04:01:04 +0530 Subject: [PATCH 5/6] API query options - Deprecating options (from/to) in API `/addr/:addr` - Adding option `after` in APIs `/addr/:addr` and `/addrs/:addrs/txs`, - Adding response value `lastItem` to API `/addrs/:addrs/txs` --- lib/addresses.js | 51 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/lib/addresses.js b/lib/addresses.js index 1e2ef84..0ecbd26 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -24,9 +24,14 @@ AddressController.prototype.show = function(req, res) { self.common.bindStopFlagOnClose(res, options); + /*DEPRECATED if (req.query.from && req.query.to) { options.from = parseInt(req.query.from); options.to = parseInt(req.query.to); + }*/ + + if (req.query.after) { + options.after = req.query.after; } self._address.getAddressSummary(req.addr, options, function(err, data) { @@ -46,9 +51,8 @@ AddressController.prototype.show_ws = function(ws, req) { var self = this; var options = { noTxList: true }; - if (req.query.from && req.query.to) { - options.from = parseInt(req.query.from); - options.to = parseInt(req.query.to); + if (req.query.after) { + options.after = req.query.after; } self.common.bindStopFlagOnClose(ws, options); @@ -282,11 +286,19 @@ AddressController.prototype.multiutxo = function(req, res) { AddressController.prototype.multitxs = function(req, res) { var self = this; - var options = { - from: parseInt(req.query.from) || parseInt(req.body.from) || 0 - }; + var options = {}; - options.to = parseInt(req.query.to) || parseInt(req.body.to) || parseInt(options.from) + 10; + options.after = req.query.after || req.body.after || undefined; + + //Temporary support + if(req.query.from || req.body.from) { + options.from = parseInt(req.query.from) || parseInt(req.body.from) || undefined; + } + + //Temporary support + if(req.query.to || req.body.to) { + options.to = parseInt(req.query.to) || parseInt(req.body.to) || undefined; + } self.common.bindStopFlagOnClose(res, options); @@ -306,8 +318,9 @@ AddressController.prototype.multitxs = function(req, res) { var ret = { totalItems: result.totalCount, - from: options.from, - to: Math.min(options.to, result.totalCount), + lastItem: items.find(a => a.confirmations !== 0), //assuming items is recent tx first order + //from: options.from, + //to: Math.min(options.to, result.totalCount), items: items }; @@ -320,11 +333,11 @@ AddressController.prototype.multitxs = function(req, res) { AddressController.prototype.multitxs_ws = function(ws, req) { var self = this; - var options = { - from: parseInt(req.query.from) || parseInt(req.body.from) || 0 - }; + var options = {}; - options.to = parseInt(req.query.to) || parseInt(req.body.to) || parseInt(options.from) + 10; + if (req.query.after) { + options.after = req.query.after; + } options.txNotNeeded = true; @@ -332,6 +345,8 @@ AddressController.prototype.multitxs_ws = function(ws, req) { self.common.bindStopFlagOnClose(ws, options); + var lastItem = {id: '', height: 0}; + self._address.getAddressHistory(req.addrs, options, function (err, data) { if(err) { return self.common.handleErrors_ws(err, ws, false); @@ -342,6 +357,13 @@ AddressController.prototype.multitxs_ws = function(ws, req) { if(err) { return self.common.handleErrors_ws(err, ws, false); } + + //finding the last key (useful for `after` option on next request call) + if(data.confirmations) + if(lastItem.height < data.blockheight || (lastItem.height == data.blockheight && lastItem.id < data.txid)){ + lastItem.id = data.txid; + lastItem.height = data.blockheight; + } ws.send({data}) @@ -355,8 +377,7 @@ AddressController.prototype.multitxs_ws = function(ws, req) { var ret = { totalItems: result.totalCount, - from: options.from, - to: Math.min(options.to, result.totalCount) + lastItem: lastItem.id } if(ws.readyState === ws.OPEN){ From 49356ef46c7864e06ee2fa5c0afe074034454b36 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sun, 5 Feb 2023 23:36:25 +0530 Subject: [PATCH 6/6] Bug fix - Fixed: lastItem give tx details instead of txid --- lib/addresses.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/lib/addresses.js b/lib/addresses.js index 0ecbd26..a7ea7e2 100644 --- a/lib/addresses.js +++ b/lib/addresses.js @@ -316,9 +316,12 @@ AddressController.prototype.multitxs = function(req, res) { return self.common.handleErrors(err, res); } + var lastItem = items.find(a => a.confirmations !== 0), //assuming items is recent tx first order + lastItem = typeof lastItem === 'object' ? lastItem.txid: undefined + var ret = { totalItems: result.totalCount, - lastItem: items.find(a => a.confirmations !== 0), //assuming items is recent tx first order + lastItem: lastItem, //from: options.from, //to: Math.min(options.to, result.totalCount), items: items @@ -352,20 +355,20 @@ AddressController.prototype.multitxs_ws = function(ws, req) { return self.common.handleErrors_ws(err, ws, false); } - self.txController.transformTransaction(tx, transformOptions, function(err, data){ + self.txController.transformTransaction(data, transformOptions, function(err, tx){ if(err) { return self.common.handleErrors_ws(err, ws, false); } //finding the last key (useful for `after` option on next request call) - if(data.confirmations) - if(lastItem.height < data.blockheight || (lastItem.height == data.blockheight && lastItem.id < data.txid)){ - lastItem.id = data.txid; - lastItem.height = data.blockheight; + if(tx.confirmations) + if(lastItem.height < tx.blockheight || (lastItem.height == tx.blockheight && lastItem.id < tx.txid)){ + lastItem.id = tx.txid; + lastItem.height = tx.blockheight; } - ws.send({data}) + ws.send({data: tx}) });