From 0bdf806d36bf5fcfb128cf6def49b52db96fa43d Mon Sep 17 00:00:00 2001 From: sairajzero Date: Mon, 7 Sep 2020 21:27:34 +0530 Subject: [PATCH] update stdop and beautify --- app/index.html | 812 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 535 insertions(+), 277 deletions(-) diff --git a/app/index.html b/app/index.html index 25e500d..3aab935 100644 --- a/app/index.html +++ b/app/index.html @@ -7624,25 +7624,35 @@ Bitcoin.Util = { //Returns public-key from private-key getPubKeyHex: function (privateKeyHex) { + if (!privateKeyHex) + return null; var key = new Bitcoin.ECKey(privateKeyHex); - if (key.priv == null) { - alert("Invalid Private key"); - return; - } + if (key.priv == null) + return null; key.setCompressed(true); var pubkeyHex = key.getPubKeyHex(); return pubkeyHex; }, - //Returns flo-ID from public-key - getFloIDfromPubkeyHex: function (pubkeyHex) { - var key = new Bitcoin.ECKey().setPub(pubkeyHex); - var floID = key.getBitcoinAddress(); - return floID; + //Returns flo-ID from public-key or private-key + getFloID: function (keyHex) { + if(!pubkeyHex) + return null; + try { + var key = new Bitcoin.ECKey(privateKeyHex); + if (key.priv == null) + key.setPub(pubkeyHex); + var floID = key.getBitcoinAddress(); + return floID; + } catch (e) { + return null; + } }, //Verify the private-key for the given public-key or flo-ID verifyPrivKey: function (privateKeyHex, pubKey_floID, isfloID = true) { + if(!privateKeyHex || !pubKey_floID) + return false; try { var key = new Bitcoin.ECKey(privateKeyHex); if (key.priv == null) @@ -7661,6 +7671,8 @@ Bitcoin.Util = { //Check if the given Address is valid or not validateAddr: function (inpAddr) { + if(!inpAddr) + return false; try { var addr = new Bitcoin.Address(inpAddr); return true; @@ -7698,7 +7710,7 @@ Bitcoin.Util = { } return false; } catch { - return false + return false; } } } @@ -7745,7 +7757,7 @@ Bitcoin.Util = { //Promised function to get data from API promisedAPI: function (apicall) { return new Promise((resolve, reject) => { - //console.log(apicall) + console.log(apicall) this.util.fetch_api(apicall) .then(result => resolve(result)) .catch(error => reject(error)); @@ -7762,22 +7774,24 @@ Bitcoin.Util = { }, //Write Data into blockchain - writeData: function (senderAddr, Data, PrivKey, receiverAddr = floGlobals.adminID) { + writeData: function (senderAddr, data, privKey, receiverAddr = floGlobals.adminID) { return new Promise((resolve, reject) => { - this.sendTx(senderAddr, receiverAddr, floGlobals.sendAmt, PrivKey, Data) + if (typeof data != "string") + data = JSON.stringify(data); + this.sendTx(senderAddr, receiverAddr, floGlobals.sendAmt, privKey, data) .then(txid => resolve(txid)) .catch(error => reject(error)) }); }, //Send Tx to blockchain - sendTx: function (senderAddr, receiverAddr, sendAmt, PrivKey, floData = '') { + sendTx: function (senderAddr, receiverAddr, sendAmt, privKey, floData = '') { return new Promise((resolve, reject) => { if (!floCrypto.validateAddr(senderAddr)) reject(`Invalid address : ${senderAddr}`); else if (!floCrypto.validateAddr(receiverAddr)) reject(`Invalid address : ${receiverAddr}`); - if (PrivKey.length < 1 || !floCrypto.verifyPrivKey(PrivKey, senderAddr)) + if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr)) reject("Invalid Private key!"); else if (typeof sendAmt !== 'number' || sendAmt <= 0) reject(`Invalid sendAmt : ${sendAmt}`); @@ -7801,8 +7815,8 @@ Bitcoin.Util = { var change = utxoAmt - sendAmt - fee; if (change > 0) trx.addoutput(senderAddr, change); - trx.addflodata(floData); - var signedTxHash = trx.sign(PrivKey, 1); + trx.addflodata(floData.replace(/\n/g, ' ')); + var signedTxHash = trx.sign(privKey, 1); this.broadcastTx(signedTxHash) .then(txid => resolve(txid)) .catch(error => reject(error)) @@ -7812,11 +7826,237 @@ Bitcoin.Util = { }); }, + //merge all UTXOs of a given floID into a single UTXO + mergeUTXOs: function (floID, privKey, floData = '') { + return new Promise((resolve, reject) => { + if (!floCrypto.validateAddr(floID)) + return reject(`Invalid floID`); + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject("Invalid Private Key") + + var trx = bitjs.transaction(); + var utxoAmt = 0.0; + var fee = floGlobals.fee; + this.promisedAPI(`api/addr/${floID}/utxo`).then(utxos => { + for (var i = utxos.length - 1; i >= 0; i--) { + if (utxos[i].confirmations) { + trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i] + .scriptPubKey) + utxoAmt += utxos[i].amount; + } + } + trx.addoutput(floID, utxoAmt - fee); + trx.addflodata(floData.replace(/\n/g, ' ')); + var signedTxHash = trx.sign(privKey, 1); + this.broadcastTx(signedTxHash) + .then(txid => resolve(txid)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }) + }, + + /**Write data into blockchain from (and/or) to multiple floID + * @param {Array} senderPrivKeys List of sender private-keys + * @param {string} data FLO data of the txn + * @param {Array} receivers List of receivers + * @param {boolean} preserveRatio (optional) preserve ratio or equal contribution + * @return {Promise} + */ + writeDataMultiple: function (senderPrivKeys, data, receivers = [floGlobals.adminID], preserveRatio = true) { + return new Promise((resolve, reject) => { + if (!Array.isArray(senderPrivKeys)) + return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array") + if (!preserveRatio) { + let tmp = {}; + let amount = (floGlobals.sendAmt * receivers.length) / senderPrivKeys.length; + senderPrivKeys.forEach(key => tmp[key] = amount); + senderPrivKeys = tmp + } + if (!Array.isArray(receivers)) + return reject("Invalid receivers: Receivers must be Array") + else { + let tmp = {}; + let amount = floGlobals.sendAmt; + receivers.forEach(floID => tmp[floID] = amount); + receivers = tmp + } + if (typeof data != "string") + data = JSON.stringify(data); + this.sendTxMultiple(senderPrivKeys, receivers, data) + .then(txid => resolve(txid)) + .catch(error => reject(error)) + }) + }, + + /**Send Tx from (and/or) to multiple floID + * @param {Array or Object} senderPrivKeys List of sender private-key (optional: with coins to be sent) + * @param {Object} receivers List of receivers with respective amount to be sent + * @param {string} floData FLO data of the txn + * @return {Promise} + */ + sendTxMultiple: function (senderPrivKeys, receivers, floData = '') { + return new Promise((resolve, reject) => { + + let senders = {}, + preserveRatio; + //check for argument validations + try { + let invalids = { + InvalidSenderPrivKeys: [], + InvalidSenderAmountFor: [], + InvalidReceiverIDs: [], + InvalidReceiveAmountFor: [] + } + 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)) { + senderPrivKeys.forEach(key => { + try { + if (!key) + invalids.InvalidSenderPrivKeys.push(key); + else { + let floID = floCrypto.getFloID(key); + senders[floID] = { + wif: key + } + } + } 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) + invalids.InvalidSenderPrivKeys.push(key); + else { + if (typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[ + key] <= 0) + invalids.InvalidSenderAmountFor.push(key) + else + inputVal += senderPrivKeys[key]; + let floID = floCrypto.getFloID(key); + senders[floID] = { + wif: key, + coins: senderPrivKeys[key] + } + } + } catch (error) { + invalids.InvalidSenderPrivKeys.push(key) + } + } + preserveRatio = false; + } + //Validate the receiver IDs and receive amount + for (let floID in receivers) { + if (!floCrypto.validateAddr(floID)) + invalids.InvalidReceiverIDs.push(floID) + if (typeof receivers[floID] !== 'number' || receivers[floID] <= 0) + invalids.InvalidReceiveAmountFor.push(floID) + else + outputVal += receivers[floID]; + } + //Reject if any invalids are found + for (let i in invalids) + if (!invalids[i].length) + delete invalids[i]; + 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) { + return reject(error) + } + //Get balance of senders + let promises = [] + for (let floID in senders) + promises.push(this.getBalance(floID)) + Promise.all(promises).then(results => { + let totalBalance = 0, + totalFee = floGlobals.fee, + balance = {}; + //Divide fee among sender if not for 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)) + insufficient.push(floID) + totalBalance += balance[floID]; + } + if (insufficient.length) + return reject({ + InsufficientBalance: insufficient + }) + //Calculate totalSentAmount and check if totalBalance is sufficient + let totalSendAmt = totalFee; + for (floID in receivers) + totalSendAmt += receivers[floID]; + if (totalBalance < totalSendAmt) + return reject("Insufficient total Balance") + //Get the UTXOs of the senders + let promises = [] + for (floID in senders) + promises.push(this.promisedAPI(`api/addr/${floID}/utxo`)) + Promise.all(promises).then(results => { + let wifSeq = []; + var trx = bitjs.transaction(); + for (floID in senders) { + let utxos = results.shift(); + let sendAmt; + if (preserveRatio) { + let ratio = (balance[floID] / totalBalance); + sendAmt = totalSendAmt * ratio; + } else + sendAmt = senders[floID].coins + dividedFee; + let wif = senders[floID].wif; + let utxoAmt = 0.0; + for (let i = utxos.length - 1; + (i >= 0) && (utxoAmt < sendAmt); i--) { + if (utxos[i].confirmations) { + trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i] + .scriptPubKey) + wifSeq.push(wif); + utxoAmt += utxos[i].amount; + } + } + if (utxoAmt < sendAmt) + return reject("Insufficient balance:" + floID); + let change = (utxoAmt - sendAmt); + if (change > 0) + trx.addoutput(floID, change); + } + for (floID in receivers) + trx.addoutput(floID, receivers[floID]); + trx.addflodata(floData.replace(/\n/g, ' ')); + for (let i = 0; i < wifSeq.length; i++) + trx.signinput(i, wifSeq[i], 1); + var signedTxHash = trx.serialize(); + this.broadcastTx(signedTxHash) + .then(txid => resolve(txid)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) + }, + //Broadcast signed Tx in blockchain using API broadcastTx: function (signedTxHash) { return new Promise((resolve, reject) => { var request = new XMLHttpRequest(); var url = this.util.serverList[this.util.curPos] + 'api/tx/send'; + console.log(url) if (signedTxHash.length < 1) reject("Empty Signature"); else { @@ -7866,7 +8106,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 => { @@ -7881,13 +8121,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; @@ -8097,7 +8341,7 @@ Bitcoin.Util = { this.connect(snID) .then(node => resolve(node)) .catch(error => { - if(reverse) + if (reverse) var nxtNode = this.kBucket.prevNode(snID); else var nxtNode = this.kBucket.nextNode(snID); @@ -8215,17 +8459,16 @@ Bitcoin.Util = { else { //Serving Users //Delete request from receiver - if(data.delete){ + if (data.delete) { let closeNode = floSupernode.kBucket.closestNode(data.from); - if (floGlobals.serveList.includes(closeNode) && - data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && - floCrypto.verifySign(JSON.stringify(data.delete), data.sign, data.pubKey)) { + if (floGlobals.serveList.includes(closeNode) && + data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && + floCrypto.verifySign(JSON.stringify(data.delete), data.sign, data.pubKey)) { //start the deletion process //indicate backup nodes to delete data } - } - else{ + } else { let closeNode = floSupernode.kBucket.closestNode(data.receiverID) if (floGlobals.serveList.includes(closeNode) && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && @@ -8241,14 +8484,15 @@ Bitcoin.Util = { type: data.type, comment: data.comment } - compactIDB.addData(floGlobals.diskList.includes(value.application) ? value.application : floGlobals + compactIDB.addData(floGlobals.diskList.includes(value.application) ? value.application : + floGlobals .defaultDisk, value, key, `SN_${closeNode}`) sendBackupData(key, value, closeNode); } } } - - if((refreshData.countdown--) <=0 ) + + if ((refreshData.countdown--) <= 0) refreshData(); } catch (error) { console.log(error.message); @@ -8268,7 +8512,7 @@ Bitcoin.Util = { const compactIDB = { setDefaultDB: function (dbName) { - this.dbName = dbName; + this.defaultDB = dbName; }, initDB: function (dbName, objectStores = {}, version = null, removeStores = []) { @@ -8313,7 +8557,7 @@ Bitcoin.Util = { }); }, - openDB: function (dbName = this.dbName) { + openDB: function (dbName = this.defaultDB) { return new Promise((resolve, reject) => { var idb = indexedDB.open(dbName); idb.onerror = (event) => reject("Error in opening IndexedDB!"); @@ -8321,7 +8565,7 @@ Bitcoin.Util = { }); }, - deleteDB: function (dbName = this.dbName) { + deleteDB: function (dbName = this.defaultDB) { return new Promise((resolve, reject) => { var deleteReq = indexedDB.deleteDatabase(dbName);; deleteReq.onerror = (event) => reject("Error deleting database!"); @@ -8329,7 +8573,7 @@ Bitcoin.Util = { }); }, - writeData: function (obsName, data, key = false, dbName = this.dbName) { + writeData: function (obsName, data, key = false, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -8343,7 +8587,7 @@ Bitcoin.Util = { }); }, - addData: function (obsName, data, key = false, dbName = this.dbName) { + addData: function (obsName, data, key = false, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -8357,7 +8601,7 @@ Bitcoin.Util = { }); }, - removeData: function (obsName, key, dbName = this.dbName) { + removeData: function (obsName, key, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -8383,7 +8627,7 @@ Bitcoin.Util = { }); }, - readData: function (obsName, key, dbName = this.dbName) { + readData: function (obsName, key, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readonly").objectStore(obsName); @@ -8397,7 +8641,7 @@ Bitcoin.Util = { }); }, - readAllData: function (obsName, dbName = this.dbName) { + readAllData: function (obsName, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readonly").objectStore(obsName); @@ -8419,7 +8663,7 @@ Bitcoin.Util = { }); }, - searchData: function (obsName, options = {}, dbName = this.dbName) { + searchData: function (obsName, options = {}, dbName = this.defaultDB) { options.lowerKey = options.atKey || options.lowerKey || 0 options.upperKey = options.atKey || options.upperKey || false options.patternEval = options.patternEval || ((k, v) => { @@ -8473,7 +8717,8 @@ Bitcoin.Util = { if (myFloID in floGlobals.supernodes) { initIndexedDBforSupernodeDataStorage(myFloID).then(result => { console.log(result) - refreshData.countdown = floGlobals.supernodeConfig.refreshDelay; + refreshData.countdown = floGlobals.supernodeConfig + .refreshDelay; floSupernode.initSupernode(serverPwd, myFloID).then( async result => { console.log(result) @@ -8481,13 +8726,13 @@ Bitcoin.Util = { floGlobals.storedList.push(myFloID) await sleep(5000); connectToAllBackupSupernode() - .then(result => console.log(result)) - .catch(error => console.error(error)) - .finally(async _ => { - console.log(result) - await sleep(2000); - indicateSupernodeUp(); - }) + .then(result => console.log(result)) + .catch(error => console.error(error)) + .finally(async _ => { + console.log(result) + await sleep(2000); + indicateSupernodeUp(); + }) }).catch(error => console.error(error)) }).catch(error => console.error(error)) } @@ -8653,7 +8898,7 @@ Bitcoin.Util = { "comment" ]; var idbObj = {} - for( let d of floGlobals.diskList) { + for (let d of floGlobals.diskList) { idbObj[d] = { indexes: {} } @@ -8724,56 +8969,58 @@ Bitcoin.Util = { function readSupernodeConfigFromAPI(flag = true) { return new Promise((resolve, reject) => { - compactIDB.readData("lastTx", floGlobals.adminID).then(lastTx => { - floBlockchainAPI.readData(floGlobals.adminID, { - ignoreOld: lastTx, - sendOnly: true, - pattern: "SuperNodeStorage" - }).then(result => { - let promises = [] - let newNodes = [] - let delNodes = [] - for (var i = result.data.length - 1; i >= 0; i--) { - var content = JSON.parse(result.data[i]).SuperNodeStorage; - for (sn in content.removeNodes) { - promises.push(compactIDB.removeData("supernodes", sn)) - delNodes.push(sn) - } - for (sn in content.addNodes) { - promises.push(compactIDB.writeData("supernodes", content - .addNodes[sn], sn)) - newNodes.push(sn) - } - for (c in content.config) - promises.push(compactIDB.writeData("config", content - .config[c], c)) - for (app in content.application) - promises.push(compactIDB.writeData("applications", - content.application[app], app)) + compactIDB.readData("lastTx", floGlobals.adminID).then(lastTx => { + floBlockchainAPI.readData(floGlobals.adminID, { + ignoreOld: lastTx, + sendOnly: true, + pattern: "SuperNodeStorage" + }).then(result => { + let promises = [] + let newNodes = [] + let delNodes = [] + for (var i = result.data.length - 1; i >= 0; i--) { + var content = JSON.parse(result.data[i]).SuperNodeStorage; + for (sn in content.removeNodes) { + promises.push(compactIDB.removeData("supernodes", sn)) + delNodes.push(sn) } - compactIDB.writeData("lastTx", result.totalTxs, floGlobals.adminID); - Promise.all(promises).then(results => { - readDataFromIDB().then(result => { - migrateData(newNodes, delNodes, flag).then(result => { - console.info(result) - resolve("Read Supernode Data from Blockchain") - }).catch(error => reject(error)) + for (sn in content.addNodes) { + promises.push(compactIDB.writeData("supernodes", content + .addNodes[sn], sn)) + newNodes.push(sn) + } + for (c in content.config) + promises.push(compactIDB.writeData("config", content + .config[c], c)) + for (app in content.application) + promises.push(compactIDB.writeData("applications", + content.application[app], app)) + } + compactIDB.writeData("lastTx", result.totalTxs, floGlobals.adminID); + Promise.all(promises).then(results => { + readDataFromIDB().then(result => { + migrateData(newNodes, delNodes, flag).then( + result => { + console.info(result) + resolve( + "Read Supernode Data from Blockchain") }).catch(error => reject(error)) }).catch(error => reject(error)) }).catch(error => reject(error)) }).catch(error => reject(error)) - }) + }).catch(error => reject(error)) + }) } function readDataFromIDB() { const dataList = { - supernodes: "supernodes", + supernodes: "supernodes", supernodeConfig: "config", applicationList: "applications" } - const readIDB = function(name, obs){ + const readIDB = function (name, obs) { return new Promise((res, rej) => { compactIDB.readAllData(obs).then(data => { floGlobals[name] = data; @@ -8784,12 +9031,12 @@ Bitcoin.Util = { return new Promise((resolve, reject) => { let promises = [] - for(let d in dataList) + for (let d in dataList) promises.push(readIDB(d, dataList[d])) Promise.all(promises) - .then(results => resolve("Read data from IDB")) - .catch(error => reject(error)) - }) + .then(results => resolve("Read data from IDB")) + .catch(error => reject(error)) + }) } function readAppSubAdminListFromAPI() { @@ -8841,9 +9088,9 @@ Bitcoin.Util = { /*Supernode Backup and migration functions*/ function connectToAllBackupSupernode(curNode = myFloID, i = 0) { return new Promise((resolve, reject) => { - if (floGlobals.backupNodes.length > i){ + if (floGlobals.backupNodes.length > i) { let rmNodes = floGlobals.backupNodes.splice(i, floGlobals.backupNodes.length); - for(node of rmNodes) + for (node of rmNodes) node.wsConn.close(); } if (i >= floGlobals.supernodeConfig.backupDepth) @@ -8855,15 +9102,15 @@ Bitcoin.Util = { else { let flag = false; initateBackupWebsocket(nxtNode).then(node => { - console.warn(`Connected to backup node: ${node.floID}`) - floGlobals.backupNodes[i] = node - flag = true; - }).catch(error => console.error(error)) - .finally( _ => { - connectToAllBackupSupernode(nxtNode, flag ? i + 1 : i) - .then(result => resolve(result)) - .catch(error => reject(error)) - }) + console.warn(`Connected to backup node: ${node.floID}`) + floGlobals.backupNodes[i] = node + flag = true; + }).catch(error => console.error(error)) + .finally(_ => { + connectToAllBackupSupernode(nxtNode, flag ? i + 1 : i) + .then(result => resolve(result)) + .catch(error => reject(error)) + }) } } }) @@ -8871,74 +9118,74 @@ Bitcoin.Util = { function initateBackupWebsocket(nodeID) { return new Promise((resolve, reject) => { - console.log("Attempting to connect to backupNode:", nodeID) - floSupernode.connect(nodeID).then(node => { - node.wsConn.onmessage = (evt) => { - if(evt.data === "$-") - replaceOfflineBackupNode(nodeID); - } - node.wsConn.onclose = (evt) => { - let i = floGlobals.backupNodes.map(d => d.floID).indexOf(nodeID); - if(i !== -1) - initateBackupWebsocket(nodeID) - .then(bNode => floGlobals.backupNodes[i] = bNode) - .catch(error => replaceOfflineBackupNode(nodeID)) - } - backupNode = { - floID: node.snID, - wsConn: node.wsConn - } - resolve(backupNode); - }).catch(error => reject(error)) + console.log("Attempting to connect to backupNode:", nodeID) + floSupernode.connect(nodeID).then(node => { + node.wsConn.onmessage = (evt) => { + if (evt.data === "$-") + replaceOfflineBackupNode(nodeID); + } + node.wsConn.onclose = (evt) => { + let i = floGlobals.backupNodes.map(d => d.floID).indexOf(nodeID); + if (i !== -1) + initateBackupWebsocket(nodeID) + .then(bNode => floGlobals.backupNodes[i] = bNode) + .catch(error => replaceOfflineBackupNode(nodeID)) + } + backupNode = { + floID: node.snID, + wsConn: node.wsConn + } + resolve(backupNode); + }).catch(error => reject(error)) }) } function replaceOfflineBackupNode(offlineNodeID) { //remove offline node and add the immediate next available node var index = floGlobals.backupNodes.map(d => d.floID).indexOf(offlineNodeID); - if (index === -1) //return if offineNode is not a backupNode - return + if (index === -1) //return if offineNode is not a backupNode + return floGlobals.backupNodes.splice(index, 1); //connect to next node available var len = floGlobals.backupNodes.length connectToAllBackupSupernode(len == 0 ? offlineNodeID : floGlobals.backupNodes[len - 1], len).then(result => { - console.log(result) - //inform the newly connected node to store backups of all serving - for( let sn of floGlobals.serveList){ - var sendData = { - from: myFloID, - sn_msg: { - type: "startBackupStore", - snID: sn, - time: Date.now() - } - } - sendData.sign = floCrypto.signData(JSON.stringify(sendData.sn_msg), myPrivKey) - floGlobals.backupNodes[len].wsConn.send(JSON.stringify(sendData)) - } - }).catch(error => console.error(error)) - .finally ( _ => { - if (index === 0) { - //start serving the dead node - if (floGlobals.backupNodes.length === 0){ - for(let sn in floGlobals.supernodes) - startBackupServe(sn) - } - //inform the immediate next node of the dead to start serving it - else { + console.log(result) + //inform the newly connected node to store backups of all serving + for (let sn of floGlobals.serveList) { var sendData = { from: myFloID, sn_msg: { - type: "startBackupServe", - snID: offlineNodeID, + type: "startBackupStore", + snID: sn, time: Date.now() } } sendData.sign = floCrypto.signData(JSON.stringify(sendData.sn_msg), myPrivKey) - floGlobals.backupNodes[0].wsConn.send(JSON.stringify(sendData)) + floGlobals.backupNodes[len].wsConn.send(JSON.stringify(sendData)) } - } - }) + }).catch(error => console.error(error)) + .finally(_ => { + if (index === 0) { + //start serving the dead node + if (floGlobals.backupNodes.length === 0) { + for (let sn in floGlobals.supernodes) + startBackupServe(sn) + } + //inform the immediate next node of the dead to start serving it + else { + var sendData = { + from: myFloID, + sn_msg: { + type: "startBackupServe", + snID: offlineNodeID, + time: Date.now() + } + } + sendData.sign = floCrypto.signData(JSON.stringify(sendData.sn_msg), myPrivKey) + floGlobals.backupNodes[0].wsConn.send(JSON.stringify(sendData)) + } + } + }) } function processDataFromSupernode(data) { @@ -8987,7 +9234,7 @@ Bitcoin.Util = { sn_msg: sn_msg, sign: floCrypto.signData(JSON.stringify(sn_msg), myPrivKey) } - for(let node of floGlobals.backupNodes) + for (let node of floGlobals.backupNodes) node.wsConn.send(JSON.stringify(data)) } @@ -9042,7 +9289,7 @@ Bitcoin.Util = { function requestBackupData(from, snID) { var promises = [] - for(let i in floGlobals.diskList) + for (let i in floGlobals.diskList) promises[i] = compactIDB.searchData(floGlobals.diskList[i], { lastOnly: true }, `SN_${snID}`) @@ -9062,8 +9309,8 @@ Bitcoin.Util = { } function storeBackupData(data) { - if (floGlobals.storedList.includes(data.snID) && - floSupernode.kBucket.closestNode(data.value.receiverID) === data.snID) { + if (floGlobals.storedList.includes(data.snID) && + floSupernode.kBucket.closestNode(data.value.receiverID) === data.snID) { compactIDB.addData( floGlobals.diskList.includes(data.value.application) ? data.value.application : floGlobals .defaultDisk, @@ -9074,7 +9321,7 @@ Bitcoin.Util = { function indicateSupernodeUp() { console.log("Indicating supernode is up") - if(floGlobals.backupNodes.length){ + if (floGlobals.backupNodes.length) { //inform all other nodes var data = { from: myFloID, @@ -9085,14 +9332,14 @@ Bitcoin.Util = { } data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey) let dataStr = JSON.stringify(data) - for(let sn in floGlobals.supernodes){ - if(sn !== myFloID){ + for (let sn in floGlobals.supernodes) { + if (sn !== myFloID) { floSupernode.connect(sn) - .then(node => { - node.wsConn.send(dataStr); - console.info('data sent to :'+ sn) - node.wsConn.close(); - }).catch(error => console.error(error)) + .then(node => { + node.wsConn.send(dataStr); + console.info('data sent to :' + sn) + node.wsConn.close(); + }).catch(error => console.error(error)) } } //Inform backup nodes to store self @@ -9105,8 +9352,9 @@ Bitcoin.Util = { //request self data from backup requestBackupData(floGlobals.backupNodes[0].floID, myFloID) } else { - let nodeList = floSupernode.kBucket.prevNode(myFloID, floSupernode.kBucket.supernodeKBucket.toArray().length-1) - for(sn of nodeList){ + let nodeList = floSupernode.kBucket.prevNode(myFloID, floSupernode.kBucket.supernodeKBucket.toArray() + .length - 1) + for (sn of nodeList) { startBackupStore(sn); startBackupServe(sn); } @@ -9125,81 +9373,85 @@ Bitcoin.Util = { if (floGlobals.serveList.includes(snID)) { stopBackupServe(snID); //inform the revived node to serve the other applicable dead nodes - let iNodes = floSupernode.kBucket.innerNodes(snID, myFloID) - data.sn_msg.time = Date.now(); - for (let sn of floGlobals.serveList.slice()){ - if (!iNodes.includes(sn) && sn != myFloID) { - data.sn_msg.snID = sn; - data.sn_msg.type = "startBackupStore"; - data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); - node.wsConn.send(JSON.stringify(data)) - data.sn_msg.type = "startBackupServe"; - data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); - node.wsConn.send(JSON.stringify(data)) - stopBackupServe(sn); - } - } - } - - if (floGlobals.backupNodes.length < floGlobals.supernodeConfig.backupDepth) { - //when less supernodes available, just connect to the revived node - let nxtNodes = floSupernode.kBucket.nextNode(myFloID, floSupernode.kBucket.supernodeKBucket.toArray().length) - var index = floGlobals.backupNodes.length - for (let i in floGlobals.backupNodes) { - if (snID == floGlobals.backupNodes[i].floID) { //revived node is already connected - index = false - break; - } else if (nxtNodes.indexOf(snID) < nxtNodes.indexOf(floGlobals.backupNodes[i].floID)) { - index = i; - break; - } - } - if (index !== false) { - initateBackupWebsocket(snID).then(result => { - floGlobals.backupNodes.splice(index, 0, result) // add revived node as backup node - //inform node on the list of backups to store - data.sn_msg.time = Date.now(); - data.sn_msg.type = "startBackupStore"; - for(let sn of floGlobals.serveList){ - data.sn_msg.snID = sn; - data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); - node.wsConn.send(JSON.stringify(data)) - } - }).catch(error => console.error(error)) - } - } else { - //connect to the revived node as backup if needed - let nxtNodes = floSupernode.kBucket.nextNode(myFloID, floSupernode.kBucket.supernodeKBucket.toArray() - .length) - var index = false - for (let i in floGlobals.backupNodes) { - if (snID == floGlobals.backupNodes[i].floID) //revived node is already connected - break; - else if (nxtNodes.indexOf(snID) < nxtNodes.indexOf(floGlobals.backupNodes[i].floID)) { - index = i; - break; - } - } - console.info(index) - if (index !== false) { - initateBackupWebsocket(snID).then(result => { - floGlobals.backupNodes.splice(index, 0, result) // add revived node as backup node - let rmNode = floGlobals.backupNodes.pop() // remove the last extra backup node - //inform node on the list of backups to store and inform removed node to stop storing - data.sn_msg.time = Date.now(); - for(let sn of floGlobals.serveList){ + let iNodes = floSupernode.kBucket.innerNodes(snID, myFloID) + data.sn_msg.time = Date.now(); + for (let sn of floGlobals.serveList.slice()) { + if (!iNodes.includes(sn) && sn != myFloID) { data.sn_msg.snID = sn; data.sn_msg.type = "startBackupStore"; data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); - node.wsConn.send(JSON.stringify(data)) //start for connected backup node - data.sn_msg.type = "stopBackupStore"; + node.wsConn.send(JSON.stringify(data)) + data.sn_msg.type = "startBackupServe"; data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); - rmNode.wsConn.send(JSON.stringify(data)) //stop for removed backup node + node.wsConn.send(JSON.stringify(data)) + stopBackupServe(sn); } - - }).catch(error => console.error(error)) + } + } + + if (floGlobals.backupNodes.length < floGlobals.supernodeConfig.backupDepth) { + //when less supernodes available, just connect to the revived node + let nxtNodes = floSupernode.kBucket.nextNode(myFloID, floSupernode.kBucket.supernodeKBucket + .toArray().length) + var index = floGlobals.backupNodes.length + for (let i in floGlobals.backupNodes) { + if (snID == floGlobals.backupNodes[i].floID) { //revived node is already connected + index = false + break; + } else if (nxtNodes.indexOf(snID) < nxtNodes.indexOf(floGlobals.backupNodes[i].floID)) { + index = i; + break; + } + } + if (index !== false) { + initateBackupWebsocket(snID).then(result => { + floGlobals.backupNodes.splice(index, 0, + result) // add revived node as backup node + //inform node on the list of backups to store + data.sn_msg.time = Date.now(); + data.sn_msg.type = "startBackupStore"; + for (let sn of floGlobals.serveList) { + data.sn_msg.snID = sn; + data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); + node.wsConn.send(JSON.stringify(data)) + } + }).catch(error => console.error(error)) + } + } else { + //connect to the revived node as backup if needed + let nxtNodes = floSupernode.kBucket.nextNode(myFloID, floSupernode.kBucket.supernodeKBucket + .toArray() + .length) + var index = false + for (let i in floGlobals.backupNodes) { + if (snID == floGlobals.backupNodes[i].floID) //revived node is already connected + break; + else if (nxtNodes.indexOf(snID) < nxtNodes.indexOf(floGlobals.backupNodes[i].floID)) { + index = i; + break; + } + } + console.info(index) + if (index !== false) { + initateBackupWebsocket(snID).then(result => { + floGlobals.backupNodes.splice(index, 0, + result) // add revived node as backup node + let rmNode = floGlobals.backupNodes.pop() // remove the last extra backup node + //inform node on the list of backups to store and inform removed node to stop storing + data.sn_msg.time = Date.now(); + for (let sn of floGlobals.serveList) { + data.sn_msg.snID = sn; + data.sn_msg.type = "startBackupStore"; + data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); + node.wsConn.send(JSON.stringify(data)) //start for connected backup node + data.sn_msg.type = "stopBackupStore"; + data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey); + rmNode.wsConn.send(JSON.stringify(data)) //stop for removed backup node + } + + }).catch(error => console.error(error)) + } } - } }) } @@ -9221,7 +9473,7 @@ Bitcoin.Util = { var index = floGlobals.serveList.indexOf(snID); if (index !== -1 && snID !== myFloID) { floGlobals.serveList.splice(index, 1); - if(floGlobals.backupNodes.length == floGlobals.supernodeConfig.backupDepth){ + if (floGlobals.backupNodes.length == floGlobals.supernodeConfig.backupDepth) { //indicate the last backup node to stop storing the revived's backup var lastIndex = floGlobals.backupNodes.length - 1 if (lastIndex !== -1) { @@ -9236,7 +9488,7 @@ Bitcoin.Util = { data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey) floGlobals.backupNodes[lastIndex].wsConn.send(JSON.stringify(data)) } - } + } console.warn("BackupServe stopped for " + snID); } } @@ -9245,7 +9497,7 @@ Bitcoin.Util = { if (!floGlobals.storedList.includes(snID)) { floGlobals.storedList.push(snID) initIndexedDBforSupernodeDataStorage(snID).then(result => { - if(from) + if (from) requestBackupData(from, snID); console.warn("BackupStore started for " + snID); }).catch(error => console.error(error)) @@ -9268,36 +9520,37 @@ Bitcoin.Util = { floSupernode.kBucket.launch().then(result => { intimateNodes(flag); //intimate all other nodes connectToAllBackupSupernode() - .then(result => console.log(result)) - .catch(error => console.error(error)) - .finally(async _ => { - await sleep(300000); - newNodes.forEach(node => { - //transfer data to new node if required - let prevNode = floSupernode.kBucket.prevNode(node); - if (floGlobals.serveList.includes(prevNode)) - transferData(prevNode, node) - let nextNode = floSupernode.kBucket.nextNode(node); - if (floGlobals.serveList.includes(nextNode)) - transferData(nextNode, node) + .then(result => console.log(result)) + .catch(error => console.error(error)) + .finally(async _ => { + await sleep(300000); + newNodes.forEach(node => { + //transfer data to new node if required + let prevNode = floSupernode.kBucket.prevNode(node); + if (floGlobals.serveList.includes(prevNode)) + transferData(prevNode, node) + let nextNode = floSupernode.kBucket.nextNode(node); + if (floGlobals.serveList.includes(nextNode)) + transferData(nextNode, node) + }) + delNodes.forEach(node => { + //split the data to its prev and next nodes + let KB = floSupernode.kBucket.constructKB(Object.keys( + floGlobals + .supernodes).concat(node).sort()); + let prevNode = floSupernode.kBucket.prevNode(node, 1, KB); + let nextNode = floSupernode.kBucket.nextNode(node, 1, KB); + if (floGlobals.serveList.includes(nextNode)) + splitData(node, prevNode, nextNode); + }) + resolve("Data migration") }) - delNodes.forEach(node => { - //split the data to its prev and next nodes - let KB = floSupernode.kBucket.constructKB(Object.keys(floGlobals - .supernodes).concat(node).sort()); - let prevNode = floSupernode.kBucket.prevNode(node, 1, KB); - let nextNode = floSupernode.kBucket.nextNode(node, 1, KB); - if (floGlobals.serveList.includes(nextNode)) - splitData(node, prevNode, nextNode); - }) - resolve("Data migration") - }) }).catch(error => reject(error)) }) } - function intimateNodes(flag){ - if(!flag) //skip intimate if already intimated + function intimateNodes(flag) { + if (!flag) //skip intimate if already intimated return; //intimate all nodes that there is change in supernode list var data = { @@ -9309,18 +9562,18 @@ Bitcoin.Util = { } data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), myPrivKey) let dataStr = JSON.stringify(data) - for(let sn in floGlobals.supernodes){ - if(sn !== myFloID){ + for (let sn in floGlobals.supernodes) { + if (sn !== myFloID) { floSupernode.connect(sn) - .then(node => { - node.wsConn.send(dataStr); - node.wsConn.close(); - }).catch(error => console.error(error)) + .then(node => { + node.wsConn.send(dataStr); + node.wsConn.close(); + }).catch(error => console.error(error)) } } } - function iniateRefresh(){ + function iniateRefresh() { readSupernodeConfigFromAPI(false) .then(result => console.log(result)) .catch(error => console.error(error)) @@ -9334,7 +9587,8 @@ Bitcoin.Util = { compactIDB.readAllData(obs, `SN_${snID}`).then(result => { let promises = [] for (let k in result) { - if (floSupernode.kBucket.closestNode(result[k].receiverID) === + if (floSupernode.kBucket.closestNode(result[k] + .receiverID) === toID) { var data = { from: myFloID, @@ -9345,10 +9599,12 @@ Bitcoin.Util = { value: result[k] } } - data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), + data.sign = floCrypto.signData(JSON.stringify(data + .sn_msg), myPrivKey) node.wsConn.send(JSON.stringify(data)); - promises.push(compactIDB.removeData(obs, k, `SN_${snID}`)) + promises.push(compactIDB.removeData(obs, k, + `SN_${snID}`)) } } Promise.all(promises).then(r => {}).catch(e => {}) @@ -9393,7 +9649,8 @@ Bitcoin.Util = { compactIDB.readAllData(obs, `SN_${snID}`).then(result => { let promises = [] for (let k in result) { - if (floSupernode.kBucket.closestNode(result[k].receiverID) === + if (floSupernode.kBucket.closestNode(result[k] + .receiverID) === prev) { var data = { from: myFloID, @@ -9404,7 +9661,8 @@ Bitcoin.Util = { value: result[k] } } - data.sign = floCrypto.signData(JSON.stringify(data.sn_msg), + data.sign = floCrypto.signData(JSON.stringify(data + .sn_msg), myPrivKey) node.wsConn.send(JSON.stringify(data)); } else