From 62c190567e8f44677c4b4805d97acd9aa4a9e311 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 31 Jul 2020 22:24:27 +0530 Subject: [PATCH 1/6] Adding settings and encrypted generalData feature - floDapps now reads 'settings' from blockchain - improved manageSubAdmins: doesnot send tx if addList and rmList are empty (i.e, no changes) - setApplicationSettings: used to set settings for the application. Note: existing settings are overwritten (ie, not appended) if same key is used. - sendGeneralData now supports a new option (encrypt): Encrypts the message before sending. Note: option value can be boolean true or pubKey of a floID. Passing true will encrypt the data using the default encryptionKey (default 'encryptionKey' must be set in settings). - getNextGeneralData now supports a new option (decrypt): Decrypts the message if possible. Note: option value can be true, privateKey or array of privateKeys. If boolean true is passed, decrypts using myPrivKey (loggedIn privateKey). If privateKey (string) is passed, decrypts using the given privateKey. If array of privateKeys are passed, decrypts the message using the suitable key from the given array. (for example, say messages A, B, C are encrypted for keys X, Y, Z respectively, passing [X, Z] will decrypt messages A and C. --- standard_Operations.html | 90 +++++++++++++++++++++++++++++++--------- 1 file changed, 70 insertions(+), 20 deletions(-) diff --git a/standard_Operations.html b/standard_Operations.html index 3f4b326..f5a35cf 100644 --- a/standard_Operations.html +++ b/standard_Operations.html @@ -8660,6 +8660,10 @@ Bitcoin.Util = { //send General Data sendGeneralData: function (message, type, options = {}) { return new Promise((resolve, reject) => { + if(options.encrypt){ + let encryptionKey = (options.encrypt === true) ? floGlobals.settings.encryptionKey : options.encrypt + message = floCrypto.encryptData(JSON.stringify(message), encryptionKey) + } this.sendApplicationData(message, type, options) .then(result => resolve(result)) .catch(error => reject(error)) @@ -8772,6 +8776,7 @@ Bitcoin.Util = { credentials: {}, //for Dapps subAdmins: {}, + settings: {}, appObjects: {}, vectorClock: {}, generalData: {}, @@ -8844,6 +8849,10 @@ Bitcoin.Util = { .length; k++) compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]); + if (content.settings) + for (let l in content.settings) + compactIDB.writeData("settings", content + .settings[l], l) } compactIDB.writeData("lastTx", result.totalTxs, floGlobals.adminID); @@ -8949,7 +8958,7 @@ Bitcoin.Util = { JSON.stringify(resultIndexes)) //also add a dummy privatekey to the IDB var randomPrivKey = floCrypto - .generateNewID().privKey + .generateNewID().privKey var randomThreshold = floCrypto.randInt(10, 20) writeSharesToIDB(floCrypto @@ -8964,32 +8973,33 @@ Bitcoin.Util = { }) } - const checkIfPinRequired = function(key){ + const checkIfPinRequired = function (key) { return new Promise((resolve, reject) => { - if(key.length == 52) + if (key.length == 52) resolve(key) else { inputFn("PIN/Password").then(pwd => { - try{ + try { let privKey = Crypto.AES.decrypt(key, pwd); resolve(privKey) - }catch(error){ + } catch (error) { reject("Access Denied: Incorrect PIN/Password") - } - }).catch(error => reject("Access Denied: PIN/Password required")) - } + } + }).catch(error => reject( + "Access Denied: PIN/Password required")) + } }) } return new Promise((resolve, reject) => { getPrivateKeyCredentials().then(key => { checkIfPinRequired(key).then(privKey => { - try{ + try { myPrivKey = privKey myPubKey = floCrypto.getPubKeyHex(myPrivKey) myFloID = floCrypto.getFloIDfromPubkeyHex(myPubKey) resolve('Login Credentials loaded successful') - }catch(error){ + } catch (error) { reject("Corrupted Private Key") } }).catch(error => reject(error)) @@ -9004,13 +9014,13 @@ Bitcoin.Util = { this.callStartUpFunction.completed += 1 reactor.dispatchEvent("startUpSuccessLog", `${result}\nCompleted ${this.callStartUpFunction.completed}/${this.callStartUpFunction.total} Startup functions` - ) + ) resolve(true) }).catch(error => { this.callStartUpFunction.failed += 1 reactor.dispatchEvent("startUpErrorLog", `${error}\nFailed ${this.callStartUpFunction.failed}/${this.callStartUpFunction.total} Startup functions` - ) + ) reject(false) }) }) @@ -9060,8 +9070,10 @@ Bitcoin.Util = { manageSubAdmins(adminPrivKey, addList, rmList) { return new Promise((resolve, reject) => { - if (!Array.isArray(addList)) addList = undefined; - if (!Array.isArray(rmList)) rmList = undefined; + if (!Array.isArray(addList) || !addList.length) addList = undefined; + if (!Array.isArray(rmList) || !rmList.length) rmList = undefined; + if (!addList && !rmList) + return reject("subAdmin manage list is empty") var floData = { [floGlobals.application]: { addSubAdmin: addList, @@ -9078,6 +9090,25 @@ Bitcoin.Util = { }) }, + setApplicationSettings(adminPrivKey, settings = {}) { + return new Promise((resolve, reject) => { + if (!settings || typeof settings !== "object") + return reject("Settings must be object") + var floData = { + [floGlobals.application]: { + settings: settings + } + } + var floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(adminPrivKey)) + if (floID != floGlobals.adminID) + reject('Access Denied for Admin privilege') + else + floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey) + .then(result => resolve(['Updated app settings', result])) + .catch(error => reject(error)) + }) + }, + clearCredentials: function () { return new Promise((resolve, reject) => { compactIDB.clearData('credentials').then(result => { @@ -9088,18 +9119,19 @@ Bitcoin.Util = { }) }, - securePrivKey: function(pwd){ + securePrivKey: function (pwd) { return new Promise((resolve, reject) => { let indexArr = localStorage.getItem(`${floGlobals.application}#privKey`) - if(!indexArr) + if (!indexArr) return reject("PrivKey not found"); indexArr = JSON.parse(indexArr) let encryptedKey = Crypto.AES.encrypt(myPrivKey, pwd); let threshold = indexArr.length; let shares = floCrypto.createShamirsSecretShares(encryptedKey, threshold, threshold) - let promises = []; - for(var i=0; i resolve("Private Key Secured")) .catch(error => reject(error)) @@ -9122,7 +9154,25 @@ Bitcoin.Util = { var filteredResult = [] for (var i = 0; i < floGlobals.generalData[filter].length; i++) if (floGlobals.generalData[filter][i].vectorClock > vectorClock) - filteredResult.push(floGlobals.generalData[filter][i]) + filteredResult.push(JSON.parse(JSON.stringify(floGlobals.generalData[filter][i]))) + if (options.decrypt) { + let decryptionKey = (options.decrypt === true) ? myPrivKey : options.decrypt; + if (!Array.isArray(decryptionKey)) + decryptionKey = [decryptionKey]; + filteredResult.forEach(data => { + try { + if ("secret" in data.message && "senderPublicKeyString" in data.message) { + for (let key of decryptionKey) { + try { + let tmp = floCrypto.decryptData(data.message, key) + data.message = JSON.parse(tmp) + break; + } catch (error) {} + } + } + } catch (error) {} + }) + } return filteredResult } } From f6a07b20ba5ad23641c4adc005ce12c8944be674 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 1 Aug 2020 18:38:10 +0530 Subject: [PATCH 2/6] Minor Improvements - generalData now also stores sign and pubKey. - renamed startup fn readSubAdminListFromAPI to readAppConfigFromAPI. - readAppConfigFromAPI also loads settings from the IDB to floGlobals. --- standard_Operations.html | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/standard_Operations.html b/standard_Operations.html index f5a35cf..7e70b37 100644 --- a/standard_Operations.html +++ b/standard_Operations.html @@ -8606,7 +8606,9 @@ Bitcoin.Util = { floGlobals.generalData[filterStr].push({ sender: dataSet[vc].senderID, vectorClock: vc, - message: dataSet[vc].message + message: dataSet[vc].message, + sign: dataSet[vc].sign, + pubKey: dataSet[vc].pubKey }) compactIDB.writeData("generalData", floGlobals.generalData[filterStr], filterStr) floGlobals.generalVC[filterStr] = vc @@ -8828,7 +8830,7 @@ Bitcoin.Util = { }) }, - readSubAdminListFromAPI: function () { + readAppConfigFromAPI: function () { return new Promise((resolve, reject) => { compactIDB.readData("lastTx", floGlobals.adminID).then(lastTx => { floBlockchainAPI.readData(floGlobals.adminID, { @@ -8858,7 +8860,10 @@ Bitcoin.Util = { floGlobals.adminID); compactIDB.readAllData("subAdmins").then(result => { floGlobals.subAdmins = Object.keys(result); - resolve("Read subAdmins from blockchain"); + compactIDB.readAllData("settings").then(result => { + floGlobals.settings = result; + resolve("Read app configuration from blockchain"); + }) }) }) }).catch(error => reject(error)) From c3607aa58ff7fb3d602b21d7c6101603880539e7 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 5 Aug 2020 13:56:12 +0530 Subject: [PATCH 3/6] Update README.md --- README.md | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 7dc24b1..696037e 100644 --- a/README.md +++ b/README.md @@ -281,28 +281,32 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved * Resolves: Object {totalTxs, floData (Array)} ## Compact IndexedDB operations -`compactIDB` operations can be used to perform basic IndexedDB operations such as add, read/write, modify and remove.Contains following operations. +`compactIDB` operations can be used to perform basic IndexedDB operations such as add, read/write, modify and remove. These functions are asynchronous and return a promise. Contains the following operations. #### setDefaultDB compactIDB.setDefaultDB(dbName) `setDefaultDB` sets the database on which further operations will be performed. 1. dbName - This is the name of default database to be used. +* Note: this operation is neither promisified nor returns a value. It just sets the default DB #### initDB compactIDB.initDB(dbName, objectStores = {}) `initDB` initializes new IndexedDB. 1. dbName - Specifies database to be initialized. 2. objectStores - This is an object containing various objectStores to be initiazed when creating an IDB. +* Resolves: Status (string) | Rejects: error #### openDB compactIDB.openDB(dbName = this.defaultDB) `openDB` returns a promise that resolves to a default database object. 1. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: database (IDB) | Rejects: error #### deleteDB compactIDB.deleteDB(dbName = this.defaultDB) `deleteDB` deletes the specified database. 1. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: Status (string) | Rejects: error #### writeData compactIDB.writeData(obsName, data, key = false, dbName = this.defaultDB) @@ -311,6 +315,7 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved 2. data - data that has to be written in specified object store. 3. key - Primary key of the data (optional, false indicates key is autoincremented or passed in data) 4. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: Status (string) | Rejects: error #### addData compactIDB.addData(obsName, data, key = false, dbName = this.defaultDB) @@ -319,6 +324,7 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved 2. data - The data which has to be added to obeject store. 3. key - Primary key of the data (optional, false indicates key is autoincremented or passed in data) 4. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: Status (string) | Rejects: error #### removeData compactDB.removeData(obsName, key, dbName = this.defaultDB) @@ -326,12 +332,14 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved 1. obsName - Name of object store from which the data has to be removed. 2. key - Primary key of the data. 3. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: Status (string) | Rejects: error #### clearData compactDB.clearData(obsName, dbName = this.defaultDB) `clearData` clears all data in the objectStore. 1. obsName - Name of object store from which the data has to be removed. 2. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: Status (string) | Rejects: error #### readData compactDB.readData(obsName, key, dbName = this.defaultDB) @@ -339,12 +347,14 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved 1. obsName - Name of object store from which the data has to be retrieved. 2. key - Primary key of the data to read. 3. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: data (Object) | Rejects: error #### readAllData compactDB.readAllData(obsName, dbName = this.defaultDB) `readAllData` reads all the data from specified object store using IndexedDB openCursor method. 1. obsName - Name of object store from which the data has to be retrieved. 2. dbName - Name of the database (optional, uses defaultDB if not specified) +* Resolves: data (Object) | Rejects: error ## FLO Supernode module This module contains functions that interact with the supernode to send and retrive data in the backend. Use floClouldAPI operations to send and receive data for application. @@ -358,11 +368,13 @@ This module contains functions that interact with the supernode to send and retr 1. message - data to be sent 2. type - type of the data 3. options - (optional, options detailed at end of module) +* Resolves: Sent-Status (string) | Rejects: error #### requestApplicationData floCloudAPI.requestApplicationData(options = {}) `requestApplicationData` requests application data from the cloud. 1. options - (optional, options detailed at end of module) +* Resolves: data (Object) | Rejects: error #### sendGeneralData floCloudAPI.sendGeneralData(message, type, options = {}) @@ -370,12 +382,14 @@ This module contains functions that interact with the supernode to send and retr 1. message - data to be sent 2. type - type of the data 3. options - (optional, options detailed at end of module) +* Resolves: Sent-Status (string) | Rejects: error #### requestGeneralData floCloudAPI.requestGeneralData(type, options = {}) `requestGeneralData` requests application data from the cloud. 1. type - type of the data 2. options - (optional, options detailed at end of module) +* Resolves: Status (string) | Rejects: error #### resetObjectData floCloudAPI.resetObjectData(objectName, options = {}) @@ -383,6 +397,7 @@ This module contains functions that interact with the supernode to send and retr 1. objectName - Name of the objectData to be reset 2. options - (optional, options detailed at end of module) Note: value of objectData is taken from floGlobals +* Resolves: Sent-Status (string) | Rejects: error #### updateObjectData floCloudAPI.updateObjectData(objectName, options = {}) @@ -390,12 +405,14 @@ Note: value of objectData is taken from floGlobals 1. objectName - Name of the objectData to be updated 2. options - (optional, options detailed at end of module) Note: value of objectData is taken from floGlobals +* Resolves: Sent-Status (string) | Rejects: error #### requestObjectData floCloudAPI.requestObjectData(objectName, options = {}) `requestObjectData` requests application data from the cloud. 1. objectName - Name of the objectData to be requested 2. options - (optional, options detailed at end of module) +* Resolves: Status (string) | Rejects: error #### options: * send options: From 58bee2e9db37d91c7d7b71fbf435294945d82e3e Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 7 Aug 2020 04:19:38 +0530 Subject: [PATCH 4/6] minor bug fix - floBlockchainAPI.readData's option pattern ll now test for JSON key matching the pattern - startUp fn readAppConfigFromAPI checks if the content is a object or not to prevent errors --- standard_Operations.html | 104 +++++++++++++++++++++++---------------- 1 file changed, 61 insertions(+), 43 deletions(-) diff --git a/standard_Operations.html b/standard_Operations.html index 7e70b37..2ee9c6f 100644 --- a/standard_Operations.html +++ b/standard_Operations.html @@ -7400,7 +7400,7 @@ Bitcoin.Util = { var change = utxoAmt - sendAmt - fee; if (change > 0) trx.addoutput(senderAddr, change); - trx.addflodata(floData.replace(/\n/g,' ')); + trx.addflodata(floData.replace(/\n/g, ' ')); var signedTxHash = trx.sign(privKey, 1); this.broadcastTx(signedTxHash) .then(txid => resolve(txid)) @@ -7431,7 +7431,7 @@ Bitcoin.Util = { } } trx.addoutput(floID, utxoAmt - fee); - trx.addflodata(floData.replace(/\n/g,' ')); + trx.addflodata(floData.replace(/\n/g, ' ')); var signedTxHash = trx.sign(privKey, 1); this.broadcastTx(signedTxHash) .then(txid => resolve(txid)) @@ -7447,11 +7447,11 @@ Bitcoin.Util = { * @param {boolean} preserveRatio (optional) preserve ratio or equal contribution * @return {Promise} */ - writeDataMultiple: function (senderPrivKeys, data, receivers = [floGlobals.adminID], preserveRatio = true){ + writeDataMultiple: function (senderPrivKeys, data, receivers = [floGlobals.adminID], preserveRatio = true) { return new Promise((resolve, reject) => { - if (!Array.isArray(senderPrivKeys)) + if (!Array.isArray(senderPrivKeys)) return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array") - if(!preserveRatio){ + if (!preserveRatio) { let tmp = {}; let amount = (floGlobals.sendAmt * receivers.length) / senderPrivKeys.length; senderPrivKeys.forEach(key => tmp[key] = amount); @@ -7481,54 +7481,59 @@ Bitcoin.Util = { */ sendTxMultiple: function (senderPrivKeys, receivers, floData = '') { return new Promise((resolve, reject) => { - - let senders = {}, preserveRatio; + + let senders = {}, + preserveRatio; //check for argument validations - try{ + try { let invalids = { InvalidSenderPrivKeys: [], InvalidSenderAmountFor: [], InvalidReceiverIDs: [], InvalidReceiveAmountFor: [] } - let inputVal = 0, outputVal = 0; + let inputVal = 0, + outputVal = 0; //Validate sender privatekeys (and send amount if passed) //conversion when only privateKeys are passed (preserveRatio mode) - if(Array.isArray(senderPrivKeys)){ + if (Array.isArray(senderPrivKeys)) { senderPrivKeys.forEach(key => { - try{ - if(!key) + try { + if (!key) invalids.InvalidSenderPrivKeys.push(key); - else{ - let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(key)); + else { + let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto + .getPubKeyHex(key)); senders[floID] = { wif: key } } - }catch(error){ + } catch (error) { invalids.InvalidSenderPrivKeys.push(key) } }) preserveRatio = true; } //conversion when privatekeys are passed with send amount - else{ - for(let key in senderPrivKeys){ - try{ - if(!key) + else { + for (let key in senderPrivKeys) { + try { + if (!key) invalids.InvalidSenderPrivKeys.push(key); - else{ - if(typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[key] <= 0) + else { + if (typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[ + key] <= 0) invalids.InvalidSenderAmountFor.push(key) else inputVal += senderPrivKeys[key]; - let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(key)); + let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex( + key)); senders[floID] = { wif: key, coins: senderPrivKeys[key] } } - }catch(error){ + } catch (error) { invalids.InvalidSenderPrivKeys.push(key) } } @@ -7550,9 +7555,10 @@ Bitcoin.Util = { if (Object.keys(invalids).length) return reject(invalids); //Reject if given inputVal and outputVal are not equal - if(!preserveRatio && inputVal != outputVal) - return reject(`Input Amount (${inputVal}) not equal to Output Amount (${outputVal})`) - }catch(error){ + if (!preserveRatio && inputVal != outputVal) + return reject( + `Input Amount (${inputVal}) not equal to Output Amount (${outputVal})`) + } catch (error) { return reject(error) } //Get balance of senders @@ -7564,18 +7570,22 @@ Bitcoin.Util = { totalFee = floGlobals.fee, balance = {}; //Divide fee among sender if not for preserveRatio - if(!preserveRatio) + if (!preserveRatio) var dividedFee = totalFee / Object.keys(senders).length; //Check if balance of each sender is sufficient enough let insufficient = []; for (let floID in senders) { balance[floID] = parseFloat(results.shift()); - if (isNaN(balance[floID]) || (preserveRatio && balance[floID] <= totalFee) || (!preserveRatio && balance[floID] < senders[floID].coins + dividedFee)) + if (isNaN(balance[floID]) || (preserveRatio && balance[floID] <= + totalFee) || (!preserveRatio && balance[floID] < senders[floID] + .coins + dividedFee)) insufficient.push(floID) totalBalance += balance[floID]; } if (insufficient.length) - return reject({InsufficientBalance: insufficient}) + return reject({ + InsufficientBalance: insufficient + }) //Calculate totalSentAmount and check if totalBalance is sufficient let totalSendAmt = totalFee; for (floID in receivers) @@ -7592,10 +7602,10 @@ Bitcoin.Util = { for (floID in senders) { let utxos = results.shift(); let sendAmt; - if(preserveRatio){ + if (preserveRatio) { let ratio = (balance[floID] / totalBalance); sendAmt = totalSendAmt * ratio; - } else + } else sendAmt = senders[floID].coins + dividedFee; let wif = senders[floID].wif; let utxoAmt = 0.0; @@ -7616,7 +7626,7 @@ Bitcoin.Util = { } for (floID in receivers) trx.addoutput(floID, receivers[floID]); - trx.addflodata(floData.replace(/\n/g,' ')); + trx.addflodata(floData.replace(/\n/g, ' ')); for (let i = 0; i < wifSeq.length; i++) trx.signinput(i, wifSeq[i], 1); var signedTxHash = trx.serialize(); @@ -7698,13 +7708,17 @@ Bitcoin.Util = { if (options.sentOnly && response.items[i].vin[0].addr !== addr) continue; - if (options.pattern && !response.items[i].floData - .startsWith(options.pattern, 0) && !response.items[i] - .floData.startsWith(options.pattern, 2)) - continue; - if (options.contains && !response.items[i].floData.includes( - options.contains)) - continue; + if (options.pattern) { + try { + let jsonContent = JSON.parse(response.items[i] + .floData) + if (!Object.keys(jsonContent).includes(options + .pattern)) + continue; + } catch (error) { + continue; + } + } if (options.filter && !options.filter(response.items[i] .floData)) continue; @@ -8841,6 +8855,8 @@ Bitcoin.Util = { for (var i = result.data.length - 1; i >= 0; i--) { var content = JSON.parse(result.data[i])[floGlobals .application]; + if (!content || typeof content !== "object") + continue; if (Array.isArray(content.removeSubAdmin)) for (var j = 0; j < content.removeSubAdmin .length; j++) @@ -8860,10 +8876,12 @@ Bitcoin.Util = { floGlobals.adminID); compactIDB.readAllData("subAdmins").then(result => { floGlobals.subAdmins = Object.keys(result); - compactIDB.readAllData("settings").then(result => { - floGlobals.settings = result; - resolve("Read app configuration from blockchain"); - }) + compactIDB.readAllData("settings").then( + result => { + floGlobals.settings = result; + resolve( + "Read app configuration from blockchain"); + }) }) }) }).catch(error => reject(error)) From fe1ce64562fe6a4ce5c8d5c81aa1d848ba56cfe6 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 7 Aug 2020 04:34:03 +0530 Subject: [PATCH 5/6] minor change - floBlockchainAPI's readData: changing default limit option value from 1000 to no limit --- standard_Operations.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard_Operations.html b/standard_Operations.html index 2ee9c6f..eee91e9 100644 --- a/standard_Operations.html +++ b/standard_Operations.html @@ -7693,7 +7693,7 @@ Bitcoin.Util = { filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'}) */ readData: function (addr, options = {}) { - options.limit = options.limit | 1000 + options.limit = options.limit | 0 options.ignoreOld = options.ignoreOld | 0 return new Promise((resolve, reject) => { this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { From 5e8f0dddb5f0c7598a0ed1e1db5085d44615528a Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 7 Aug 2020 04:34:14 +0530 Subject: [PATCH 6/6] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b7fab07..4c92e59 100644 --- a/README.md +++ b/README.md @@ -282,11 +282,10 @@ Note: If passed as Array, then ratio of the balance of the senders are preserved `readData` reads FLO data from transactions of specified address 1. addr - FLO address for which the transactions data has to be read. 2. options - Contains options for filter data from transactions. - - limit : maximum number of filtered data (default = 1000, negative = no limit) + - limit : maximum number of filtered data (default = no limit) - ignoreOld : ignore old transactions (default = 0) - sentOnly : filters only sent data - - pattern : filters data that starts with a pattern - - contains : filters data that contains a string + - pattern : filters data that contains pattern as an object key in the JSON string - filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'}) * Resolves: Object {totalTxs, floData (Array)}