From 5e6d47a042ac671e2665f2013927e8fee780be9b Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 2 Nov 2019 01:55:13 +0530 Subject: [PATCH 01/30] Supernode status detection --- index.html | 128 +++++++++++++++++++++++++++++++++++++++++++++-- util/websocket.c | 7 +++ 2 files changed, 131 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 20dbaa2..d907bdd 100644 --- a/index.html +++ b/index.html @@ -5226,6 +5226,53 @@ } }); }, + + getPrevSupernode: function(flo_addr, n = 1, KB = this.supernodeKBucket){ + return new Promise((resolve, reject) => { + try { + let isFloIdUint8 = flo_addr instanceof Uint8Array; + if (!isFloIdUint8) + flo_addr = this.floIdToKbucketId(flo_addr); + const KA = KB.toArray(); + let pos = KA.indexOf(flo_addr) + var prevSupernode = [] + for(var i = 0; i < n; i++){ + if(pos - i < 0) + var prev = pos - i + KA.length + else + var prev = pos - i + prevSupernode.push(KA[prev]) + } + resolve(prevSupernode); + } catch (error) { + reject(error); + } + }); + }, + + getNextSupernode: function(flo_addr, n = 1, KB = this.supernodeKBucket){ + return new Promise((resolve, reject) => { + try { + let isFloIdUint8 = flo_addr instanceof Uint8Array; + if (!isFloIdUint8) + flo_addr = this.floIdToKbucketId(flo_addr); + const KA = KB.toArray(); + let pos = KA.indexOf(flo_addr) + var nextSupernode = [] + for(var i = 0; i < n; i++){ + if(pos + i >= KA.length) + var next = pos + i - KA.length + else + var next = pos + i + nextSupernode.push(KA[next]) + } + resolve(nextSupernode); + } catch (error) { + reject(error); + } + }); + }, + determineClosestSupernode: function (flo_addr, n = 1, KB = this.supernodeKBucket) { return new Promise((resolve, reject) => { try { @@ -5376,10 +5423,9 @@ try { data = JSON.parse(data) floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { - if (result[0].floID != myFloID) - return; - if (data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && floCrypto.verifySign( - JSON.stringify(data.message), data.sign, data.pubKey)) { + if (result[0].floID == myFloID + && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) + && floCrypto.verifySign(JSON.stringify(data.message), data.sign, data.pubKey)){ if (floGlobals.storageList.includes(data.application)) var table = data.application; else @@ -6047,7 +6093,81 @@ }).catch(error => reject(error)) }) } + + function connectToBackupSupernode(){ + return new Promise((resolve,reject) => { + floSupernode.kBucket.getNextSupernode(myFloID).then(backupNodeID => { + reactor.dispatchEvent("initate_backup_websocket",backupNodeID) + }).catch(error => reactor.dispatchEvent) + }) + } + diff --git a/util/websocket.c b/util/websocket.c index c5b1c2c..eb63899 100644 --- a/util/websocket.c +++ b/util/websocket.c @@ -94,6 +94,10 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { case MG_EV_WEBSOCKET_HANDSHAKE_DONE: { /*New websocket connection*/ display(nc, "+Connected+"); + if(supernode_client!=NULL) + unicast(nc, mg_mk_str("$+")); + else + unicast(nc, mg_mk_str("$-")); break; } case MG_EV_WEBSOCKET_FRAME: { @@ -106,6 +110,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { if(!strcmp(pass,server_pwd)){ if(supernode_client!=NULL) unicast(supernode_client,mg_mk_str("$Another login is encountered! Please close/refresh this window")); + else + broadcast(nc, mg_mk_str("$+")); supernode_client = nc; unicast(supernode_client,mg_mk_str("$Access Granted!")); display(nc, "*Became SuperNode*"); @@ -129,6 +135,7 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { if(nc == supernode_client){ supernode_client = NULL; display(nc,"!SuperNode Disconnected!"); + broadcast(nc, mg_mk_str("$-")); }else display(nc, "-Disconnected-"); break; From 95a3c9a1278754a1337eb432f459c9d6cc8b57d1 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Tue, 5 Nov 2019 21:41:47 +0530 Subject: [PATCH 02/30] Initializing backup feature requirements Restructuring IDB to provide more reliable storage for upcoming backup features --- index.html | 125 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 49 deletions(-) diff --git a/index.html b/index.html index d907bdd..4ba3789 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,10 @@ //Required for Supernode operations supernodes: {}, //each supnernode must be stored as floID : {uri:,pubKey:} storageList : ["General"], - defaultStorage : "General" + defaultStorage : "General", + serveList : [], + backupStoredList : [], + supernodeConfig : {} } @@ -5401,16 +5404,20 @@ request = request.split(" "); requestor = request[0]; request = JSON.parse(request[1]); - var filterOptions = { - lowerKey: request.lowerVectorClock, - upperKey: request.upperVectorClock, - lastOnly: request.mostRecent, - atKey: request.atVectorClock, - patternEval: (k, v) => { return (v.application == request.application && (!request.receiverID || v.receiverID == request.receiverID) && (!request.comment || v.comment == request.comment) && (!request.type || v.type == request.type) && (!request.senderIDs || request.senderIDs.includes(v.senderID))) } - } - compactIDB.searchData( floGlobals.storageList.includes(request.application) ? request.application : floGlobals.defaultStorage, filterOptions) - .then(result => floSupernode.supernodeClientWS.send(`${requestor} ${JSON.stringify(result)}`)) - .catch(error => console.log(error)) + floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { + if(floGlobals.serveList.includes(result[0].floID)){ + var filterOptions = { + lowerKey: request.lowerVectorClock, + upperKey: request.upperVectorClock, + lastOnly: request.mostRecent, + atKey: request.atVectorClock, + patternEval: (k, v) => { return (v.application == request.application && (!request.receiverID || v.receiverID == request.receiverID) && (!request.comment || v.comment == request.comment) && (!request.type || v.type == request.type) && (!request.senderIDs || request.senderIDs.includes(v.senderID))) } + } + compactIDB.searchData( floGlobals.storageList.includes(request.application) ? request.application : floGlobals.defaultStorage, filterOptions, `SN_${result[0].floID}`) + .then(result => floSupernode.supernodeClientWS.send(`${requestor} ${JSON.stringify(result)}`)) + .catch(error => console.log(error)) + } + }).catch(error => console.log(error)) } catch (error) { console.log(error.message) } @@ -5422,26 +5429,29 @@ console.log('Data :', data); try { data = JSON.parse(data) - floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { - if (result[0].floID == myFloID - && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) - && floCrypto.verifySign(JSON.stringify(data.message), data.sign, data.pubKey)){ - if (floGlobals.storageList.includes(data.application)) - var table = data.application; - else - var table = floGlobals.defaultStorage; - compactIDB.addData(table, { - senderID: data.senderID, - receiverID: data.receiverID, - pubKey: data.pubKey, - message: data.message, - sign: data.sign, - application: data.application, - type: data.type, - comment: data.comment - }) - } - }) + if(!data.backup){ //Serving Users + floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { + if (floGlobals.serveList.includes(result[0].floID) + && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) + && floCrypto.verifySign(JSON.stringify(data.message), data.sign, data.pubKey)){ + if (floGlobals.storageList.includes(data.application)) + var table = data.application; + else + var table = floGlobals.defaultStorage; + var key = `${Date.now()}_${data.senderID}` + compactIDB.addData(table, { + senderID: data.senderID, + receiverID: data.receiverID, + pubKey: data.pubKey, + message: data.message, + sign: data.sign, + application: data.application, + type: data.type, + comment: data.comment + },key,`SN_${result[0].floID}`) + } + }).catch(error => console.log(error)) + } } catch (error) { console.log(error.message); } @@ -5465,7 +5475,7 @@ initDB: function (dbName, objectStores = {}) { return new Promise((resolve, reject) => { - this.dbName = this.dbName || dbName; + //this.dbName = this.dbName || dbName; var idb = indexedDB.open(dbName); idb.onerror = (event) => { reject("Error in opening IndexedDB!"); @@ -6021,45 +6031,57 @@ myPubKey = floCrypto.getPubKeyHex(myPrivKey) myFloID = floCrypto.getFloIDfromPubkeyHex(myPubKey) - initIndexedDBforSupernode().then(result => { + initIndexedDBforSupernodeConfig().then(result => { console.log(result) readSupernodeListFromAPI().then(result => { console.log(result) floSupernode.kBucket.launch().then(result => { console.log(result) if (myFloID in floGlobals.supernodes) { - serverPwd = prompt("Enter Server Pass!") - setInterval(refreshBlockchainData, 3600000); - floSupernode.initSupernode(serverPwd, myFloID) - .then(result => console.log(result)) - .catch(error => console.log(error)) + initIndexedDBforSupernodeDataStorage(myFloID).then(result => { + console.log(result) + serverPwd = prompt("Enter Server Pass!") + setInterval(refreshBlockchainData, 3600000); + floSupernode.initSupernode(serverPwd, myFloID) + .then(result => console.log(result)) + .catch(error => console.log(error)) + }) } }) }) }).catch(error => console.log(error)) } - function initIndexedDBforSupernode(){ + function initIndexedDBforSupernodeConfig(){ return new Promise((resolve, reject) => { - var IndexesList = ["senderID","receiverID","pubKey","message","sign","application","type","comment"]; - var primaryKey = "vectorClock"; var storageList = floGlobals.storageList; - var obj = { + var snObj = { lastTx:{}, + config:{}, supernodes:{ indexes:{ uri:null, pubKey:null } } } + compactIDB.setDefaultDB("SupernodeConfig") + compactIDB.initDB("SupernodeConfig", snObj) + .then(result => resolve("Initiated supernode configuration IDB")) + .catch(error => reject(error)); + }) + } + + function initIndexedDBforSupernodeDataStorage(floID){ + return new Promise((resolve, reject) => { + var indexesList = ["senderID","receiverID","pubKey","message","sign","application","type","comment"]; + var idbObj = {} for(var i=0;i resolve("Initiated supernode storage")) + compactIDB.initDB(`SN_${floID}`, idbObj) + .then(result => resolve("Initiated supernode storage IDB for "+floID)) .catch(error => reject(error)); }) } @@ -6083,11 +6105,16 @@ compactIDB.removeData("supernodes",sn); for(sn in content.addNodes) compactIDB.writeData("supernodes",content.addNodes[sn],sn); + for(c in content.config) + compactIDB.writeData("config",content.config[c],c) } compactIDB.writeData("lastTx",result.totalTxs,floGlobals.adminID); compactIDB.readAllData("supernodes").then(result => { floGlobals.supernodes = result - resolve("Read supernode from blockchain"); + compactIDB.readAllData("config").then(result => { + floGlobals.config = result + resolve("Read supernode from blockchain"); + }) }) }) }).catch(error => reject(error)) From 4f4dc0b431bb70591c57c9b7432152df29606041 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 8 Nov 2019 01:55:47 +0530 Subject: [PATCH 03/30] Backup Feature: serve and send backup added : serve as backup node added : send backup data to backup node improved connecting to nearest backup node --- index.html | 177 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 135 insertions(+), 42 deletions(-) diff --git a/index.html b/index.html index 4ba3789..fb63317 100644 --- a/index.html +++ b/index.html @@ -5429,7 +5429,7 @@ console.log('Data :', data); try { data = JSON.parse(data) - if(!data.backup){ //Serving Users + if(!data.backupMsg){ //Serving Users floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { if (floGlobals.serveList.includes(result[0].floID) && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) @@ -5439,7 +5439,7 @@ else var table = floGlobals.defaultStorage; var key = `${Date.now()}_${data.senderID}` - compactIDB.addData(table, { + var value = { senderID: data.senderID, receiverID: data.receiverID, pubKey: data.pubKey, @@ -5448,10 +5448,13 @@ application: data.application, type: data.type, comment: data.comment - },key,`SN_${result[0].floID}`) + } + compactIDB.addData(table, value ,key,`SN_${result[0].floID}`) + reactor.dispatchEvent("send_backup",{ key:key, value:value, snfloID: result[0].floID}) } }).catch(error => console.log(error)) - } + }else if(data.from in floGlobals.supernodes) + reactor.dispatchEvent("backup_message_event", data) } catch (error) { console.log(error.message); } @@ -6044,7 +6047,6 @@ setInterval(refreshBlockchainData, 3600000); floSupernode.initSupernode(serverPwd, myFloID) .then(result => console.log(result)) - .catch(error => console.log(error)) }) } }) @@ -6121,36 +6123,41 @@ }) } - function connectToBackupSupernode(){ + function connectToBackupSupernode(backupNodeID){ return new Promise((resolve,reject) => { - floSupernode.kBucket.getNextSupernode(myFloID).then(backupNodeID => { - reactor.dispatchEvent("initate_backup_websocket",backupNodeID) - }).catch(error => reactor.dispatchEvent) + initateBackupWebsocket(backupNodeID) + .then(result => resolve(result)) + .catch(error => { + console.log(error) + floSupernode.kBucket.getNextSupernode(backupNodeID).then(nextBackupNode => { + connectToBackupSupernode(nextBackupNode).then(result => resolve(result)) + }).catch(error => console.log(error)) + }) + }) + } + + function initateBackupWebsocket(backupNodeID){ + return new Promise((resolve,reject) => { + try{ + floGlobals.backupNode = { + floID: backupNodeID, + wsConn: new WebSocket("wss://" + floGlobals.supernodes[backupNodeID].uri + "/ws") + } + floGlobals.backupNode.wsConn.onopen = (evt) => { + floGlobals.backupNode.wsConn.onmessage = (ev) => reactor.dispatchEvent("backup_node_message",ev); + floGlobals.backupNode.wsConn.onclose = (ev) => reactor.dispatchEvent("backup_node_disconnected",ev); + resolve(`connnected to ${backupNodeID} ws`); + } + floGlobals.backupNode.wsConn.onerror = (evt) => reject(`${backupNodeID} ws not found`); + }catch(error){ + reject(error.message) + } }) } From 0e6c81e5ab907a653e29e664b440e679e1ba44b4 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 8 Nov 2019 22:33:17 +0530 Subject: [PATCH 04/30] backup feature : indicate_sn_up, store_backup_data, request_data Added following features: inidicate the next node when a sn is down inidicate the next node when a sn is up request backuped data when a sn comes back online --- index.html | 95 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 79 insertions(+), 16 deletions(-) diff --git a/index.html b/index.html index fb63317..dc1501b 100644 --- a/index.html +++ b/index.html @@ -5434,10 +5434,6 @@ if (floGlobals.serveList.includes(result[0].floID) && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && floCrypto.verifySign(JSON.stringify(data.message), data.sign, data.pubKey)){ - if (floGlobals.storageList.includes(data.application)) - var table = data.application; - else - var table = floGlobals.defaultStorage; var key = `${Date.now()}_${data.senderID}` var value = { senderID: data.senderID, @@ -5449,7 +5445,7 @@ type: data.type, comment: data.comment } - compactIDB.addData(table, value ,key,`SN_${result[0].floID}`) + compactIDB.addData(floGlobals.storageList.includes(value.application) ? value.application:floGlobals.defaultStorage , value ,key,`SN_${result[0].floID}`) reactor.dispatchEvent("send_backup",{ key:key, value:value, snfloID: result[0].floID}) } }).catch(error => console.log(error)) @@ -5594,7 +5590,7 @@ }, searchData: function (obsName, options = {}, dbName = this.dbName) { - options.lowerKey = options.atKey || options.lowerKey || 1 + options.lowerKey = options.atKey || options.lowerKey || 0 options.upperKey = options.atKey || options.upperKey || false options.patternEval = options.patternEval || ((k,v) => {return true}) options.lastOnly = options.lastOnly || false @@ -6045,8 +6041,16 @@ console.log(result) serverPwd = prompt("Enter Server Pass!") setInterval(refreshBlockchainData, 3600000); - floSupernode.initSupernode(serverPwd, myFloID) - .then(result => console.log(result)) + floSupernode.initSupernode(serverPwd, myFloID).then(result => { + console.log(result) + floSupernode.kBucket.getNextSupernode(myFloID).then(result => { + connectToBackupSupernode(result[0].floID).then(result => { + console.log(result) + reactor.dispatchEvent("indicate_sn_up",myFloID) + reactor.dispatchEvent("request_data",{floid:myFloID, holder:floGlobals.backupNode.floID}) + }) + }) + }) }) } }) @@ -6216,6 +6220,22 @@ floGlobals.backupNode.wsConn.send(sendData) }) + reactor.registerEvent("indicate_sn_up"); + reactor.addEventListener("indicate_sn_up", function (snfloID) { + console.log("indicate_sn_up"); + //send message to backup's backup to server for backup node + var sendData = { + from: myFloID, + backupMsg: { + type: "supernodeUp", + snfloID: snfloID, + time: Date.now() + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + floGlobals.backupNode.wsConn.send(sendData) + }) + reactor.registerEvent("send_backup"); reactor.addEventListener("send_backup", function (data) { console.log("send_backup"); @@ -6232,11 +6252,11 @@ floGlobals.backupNode.wsConn.send(sendData) }) - reactor.registerEvent("send_all_stored_backup"); - reactor.addEventListener("send_all_stored_backup", function (data) { - console.log("send_all_stored_backup"); + reactor.registerEvent("send_stored_backup"); + reactor.addEventListener("send_stored_backup", function (data) { + console.log("send_stored_backup"); try{ - var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[backupNodeID].uri + "/ws") + var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[data.from].uri + "/ws") requestorWS.onopen = (evt) => { floGlobals.storageList.forEach(obs => { compactIDB.searchData(obs, {lowerKey: data.backupMsg.lowerKey[obs]}, `SN_${data.backupMsg.snfloID}`).then(result => { @@ -6257,7 +6277,7 @@ }) } requestorWS.onmessage = (evt) => console.log(evt.data); - requestorWS.onclose = (ev) => console.log("Disconnected from "+data.from); + requestorWS.onclose = (evt) => console.log("Disconnected from "+data.from); requestorWS.onerror = (evt) => console.log("Error connecting to "+data.from); }catch(error){ console.log(error.message) @@ -6269,11 +6289,54 @@ console.log("backup_message_event"); if(floCrypto.verifySign(JSON.stringify(data.backupMsg), data.sign, floGlobals.supernodes[data.from].pubKey)){ //Backup event messages - //1. sn offline => a.(nearest backup node) start_backup_serve; b.(new farest backup node) create new backup - //2. sn back online => a.(nearest backup node) stop_backup_serve, send backup data to supernode b.(farest backup node) delete backup - //3. backup data => send_all_backup + if(data.backupMsg.type === "backupData" && floGlobals.backupStoredList.includes(data.backupMsg.snfloID)) + reactor.dispatchEvent("store_backup_data", data.backupMsg) + else if(data.backupMsg.type === "supernodeDown") + reactor.dispatchEvent("start_backup_serve", data.backupMsg.snfloID) + else if(data.backupMsg.type === "supernodeUp") + reactor.dispatchEvent("stop_backup_serve", data.backupMsg.snfloID) + else if(data.backupMsg.type === "dataRequest" && floGlobals.backupStoredList.includes(data.backupMsg.snfloID)) + reactor.dispatchEvent("send_stored_backup", data) } }) + + reactor.registerEvent("request_data"); + reactor.addEventListener("request_data", function (event) { + console.log("request_data"); + try{ + var holderWS = new WebSocket("wss://" + floGlobals.supernodes[event.holder].uri + "/ws") + holderWS.onopen = (evt) => { + var promises = [] + for(var i=0; i < floGlobals.storageList.length; i++) + promises[i] = compactIDB.searchData(floGlobals.storageList[i],{lastOnly: true},`SN_${event.floID}`) + Promise.all(promises).then(results => { + var lowerKey = {} + for(var i=0; i < results.length; i++) + for(key in results[i]) + lowerKey[floGlobals.storageList[i]] = key + var sendData = { + from: myFloID, + backupMsg = { + snfloID: event.floID, + lowerKey: lowerKey + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + }) + } + holderWS.onmessage = (evt) => console.log(evt.data); + holderWS.onclose = (evt) => console.log("Disconnected from " + event.holder); + holderWS.onerror = (evt) => console.log("Error connecting to " + event.holder); + }catch(error){ + console.log(error.message) + } + }) + + reactor.registerEvent("store_backup_data"); + reactor.addEventListener("store_backup_data", function (data) { + console.log("store_backup_data"); + compactIDB.addData(floGlobals.storageList.includes(data.value.application) ? data.value.application:floGlobals.defaultStorage , data.value ,data.key,`SN_${data.snfloID}`) + }) reactor.registerEvent("start_backup_serve"); reactor.addEventListener("start_backup_serve", function (floID) { From 48700f4b95f261b442898f1df9e8c10db48da372 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 9 Nov 2019 19:22:13 +0530 Subject: [PATCH 05/30] recompiling binary --- supernodeWSS | Bin 176480 -> 176480 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/supernodeWSS b/supernodeWSS index c2cc4a29c30e904a5b773b2a6bf16601c3d86b8f..5b551a69e790719867bfb9cf82480c92e83d45b8 100755 GIT binary patch delta 31598 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 KnYLScBK;o*Uv={U delta 31246 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 Date: Tue, 12 Nov 2019 21:15:14 +0530 Subject: [PATCH 06/30] Flow when a node dies Added the indications and processes when a noded goes offline: The previous nodes that the dead was serving ll connect to a next available node The immediate prev node informs the immediate next node to start serving the dead --- index.html | 228 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 158 insertions(+), 70 deletions(-) diff --git a/index.html b/index.html index dc1501b..3f9eb1c 100644 --- a/index.html +++ b/index.html @@ -35,7 +35,8 @@ defaultStorage : "General", serveList : [], backupStoredList : [], - supernodeConfig : {} + supernodeConfig : {}, + backupNodes : [] } @@ -6043,12 +6044,10 @@ setInterval(refreshBlockchainData, 3600000); floSupernode.initSupernode(serverPwd, myFloID).then(result => { console.log(result) - floSupernode.kBucket.getNextSupernode(myFloID).then(result => { - connectToBackupSupernode(result[0].floID).then(result => { - console.log(result) - reactor.dispatchEvent("indicate_sn_up",myFloID) - reactor.dispatchEvent("request_data",{floid:myFloID, holder:floGlobals.backupNode.floID}) - }) + connectToAllBackupSupernode().then(result => { + console.log(result) + reactor.dispatchEvent("indicate_sn_up",myFloID) + reactor.dispatchEvent("request_data",{floid:myFloID, holder:floGlobals.backupNode.floID}) }) }) }) @@ -6127,6 +6126,24 @@ }) } + function connectToAllBackupSupernode(curNode = myFloID, i = 0){ + return new Promise((resolve,reject) => { + if(i >= floGlobals.supernodeConfig.backupDepth) + resolve("Connected to all backup nodes") + else{ + floSupernode.kBucket.getNextSupernode(curNode).then(nextBackupNode => { + connectToBackupSupernode(nextBackupNode).then(backupConn => { + floGlobals.backupNodes[i] = backupConn + connectToAllBackupSupernode(backupConn.floID, i+1) + .then(result => resolve(result)) + .catch(error => reject(error)) + }) + }).catch(error => console.log(error)) + } + + }) + } + function connectToBackupSupernode(backupNodeID){ return new Promise((resolve,reject) => { initateBackupWebsocket(backupNodeID) @@ -6143,16 +6160,16 @@ function initateBackupWebsocket(backupNodeID){ return new Promise((resolve,reject) => { try{ - floGlobals.backupNode = { + var backupNode = { floID: backupNodeID, wsConn: new WebSocket("wss://" + floGlobals.supernodes[backupNodeID].uri + "/ws") } - floGlobals.backupNode.wsConn.onopen = (evt) => { - floGlobals.backupNode.wsConn.onmessage = (ev) => reactor.dispatchEvent("backup_node_message",ev); - floGlobals.backupNode.wsConn.onclose = (ev) => reactor.dispatchEvent("backup_node_disconnected",ev); - resolve(`connnected to ${backupNodeID} ws`); + backupNode.wsConn.onopen = (evt) => { + backupNode.wsConn.onmessage = (ev) => reactor.dispatchEvent("backup_node_message",{floID:backupNodeID,data:ev.data}); + backupNode.wsConn.onclose = (ev) => reactor.dispatchEvent("backup_node_disconnected",backupNodeID); + resolve(backupNode); } - floGlobals.backupNode.wsConn.onerror = (evt) => reject(`${backupNodeID} ws not found`); + backupNode.wsConn.onerror = (evt) => reject(`${backupNodeID} ws not found`); }catch(error){ reject(error.message) } @@ -6168,93 +6185,110 @@ console.log("backup_node_message"); //received message from backup node if(event.data == "$+") - reactor.dispatchEvent("backup_node_online",null); + reactor.dispatchEvent("backup_node_online",event.floID); else if (event.data == "$-") - reactor.dispatchEvent("backup_node_offline",null); + reactor.dispatchEvent("backup_node_offline",event.floID); else console.log(event.data) }) reactor.registerEvent("backup_node_disconnected"); - reactor.addEventListener("backup_node_disconnected", function (event) { + reactor.addEventListener("backup_node_disconnected", function (backupNodeID) { console.log("backup_node_disconnected"); //try to reconnect (usually disconnects due to timeout) - connectToBackupSupernode(floGlobals.backupNode.floID) + initateBackupWebsocket(backupNodeID) .then(result => console.log(result)) - .catch(error => console.log(error)) + .catch(error =>{ + console.log(error) + reactor.dispatchEvent("backup_node_offline",backupNodeID); + }) }) reactor.registerEvent("backup_node_online"); - reactor.addEventListener("backup_node_online", function (event) { + reactor.addEventListener("backup_node_online", function (backupNodeID) { console.log("backup_node_online"); //backup node is alive. //do nothing for now }) reactor.registerEvent("backup_node_offline"); - reactor.addEventListener("backup_node_offline", function (event) { + reactor.addEventListener("backup_node_offline", function (offlineNodeID) { console.log("backup_node_offline"); - //backup node is offline. - //get next available node - var offlineNodeID = floGlobals.backupNode.floID - floSupernode.kBucket.getNextSupernode(offlineNodeID).then(result => { - connectToBackupSupernode(result[0].floID).then(result => { - reactor.dispatchEvent("indicate_sn_down",offlineNodeID) - }) + //remove offline node and add the immediate next available node + var index = floGlobals.backupNodes.indexOf(offlineNodeID); + if (index !== -1) floGlobals.backupNodes.splice(index, 1); + var len = floGlobals.backupNodes.length + connectToAllBackupSupernode(floGlobals.backupNodes[len-1], len).then(result => { + console.log(result) + //inform the newly connected node to store backups of self + var sendData1 = { + from: myFloID, + backupMsg: { + type: "startBackupStore", + snfloID: myFloID, + time: Date.now() + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + floGlobals.backupNodes[len].wsConn.send(JSON.stringify(sendData1)) + //inform the immediate next node of the dead to start serving it + if(index == 0){ + var sendData2 = { + from: myFloID, + backupMsg: { + type: "startBackupServe", + snfloID: offlineNodeID, + time: Date.now() + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + floGlobals.backupNodes[0].wsConn.send(JSON.stringify(sendData2)) + } }) }) - reactor.registerEvent("indicate_sn_down"); - reactor.addEventListener("indicate_sn_down", function (snfloID) { - console.log("indicate_sn_down"); - //send message to backup's backup to server for backup node + reactor.registerEvent("send_message_to_backup_nodes"); + reactor.addEventListener("send_message_to_backup_nodes", function (backupMsg) { + console.log("send_message_to_backup_nodes"); + //send message to all connected backup nodes var sendData = { from: myFloID, - backupMsg: { - type: "supernodeDown", - snfloID: snfloID, - time: Date.now() - }, + backupMsg: backupMsg, sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) } - floGlobals.backupNode.wsConn.send(sendData) + for(var i = 0; i < floGlobals.backupNodes.length; i++) + floGlobals.backupNode[i].wsConn.send(sendData) }) reactor.registerEvent("indicate_sn_up"); reactor.addEventListener("indicate_sn_up", function (snfloID) { console.log("indicate_sn_up"); - //send message to backup's backup to server for backup node - var sendData = { - from: myFloID, - backupMsg: { - type: "supernodeUp", - snfloID: snfloID, - time: Date.now() - }, - sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + //send message to backup's backup to server for backup node (*to be rectified*) + var backupMsg = { + type: "supernodeUp", + snfloID: snfloID, + time: Date.now() } - floGlobals.backupNode.wsConn.send(sendData) + reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg) }) reactor.registerEvent("send_backup"); reactor.addEventListener("send_backup", function (data) { console.log("send_backup"); - var sendData = { - from: myFloID, - backupMsg : { - type: "backupData", - snfloID: data.snfloID, - key: data.k, - value: data.value - }, - sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + //send backup data to backup nodes + var backupMsg = { + type: "backupData", + snfloID: data.snfloID, + key: data.k, + value: data.value } - floGlobals.backupNode.wsConn.send(sendData) + reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg) }) reactor.registerEvent("send_stored_backup"); reactor.addEventListener("send_stored_backup", function (data) { console.log("send_stored_backup"); + //send stored backuped data to the requestor node try{ var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[data.from].uri + "/ws") requestorWS.onopen = (evt) => { @@ -6263,7 +6297,7 @@ for(k in result){ var sendData = { from: myFloID, - backupMsg : { + backupMsg: { type: "backupData", snfloID: data.backupMsg.snfloID, key: k, @@ -6277,8 +6311,8 @@ }) } requestorWS.onmessage = (evt) => console.log(evt.data); - requestorWS.onclose = (evt) => console.log("Disconnected from "+data.from); - requestorWS.onerror = (evt) => console.log("Error connecting to "+data.from); + requestorWS.onclose = (evt) => console.log("Disconnected from " + data.from); + requestorWS.onerror = (evt) => console.log("Error connecting to " + data.from); }catch(error){ console.log(error.message) } @@ -6288,13 +6322,23 @@ reactor.addEventListener("backup_message_event", function (data) { console.log("backup_message_event"); if(floCrypto.verifySign(JSON.stringify(data.backupMsg), data.sign, floGlobals.supernodes[data.from].pubKey)){ - //Backup event messages + //Backup event messages (most crucial part) + if(data.backupMsg.type === "backupData" && floGlobals.backupStoredList.includes(data.backupMsg.snfloID)) reactor.dispatchEvent("store_backup_data", data.backupMsg) - else if(data.backupMsg.type === "supernodeDown") + + else if(data.backupMsg.type === "startBackupServe") reactor.dispatchEvent("start_backup_serve", data.backupMsg.snfloID) - else if(data.backupMsg.type === "supernodeUp") + + else if(data.backupMsg.type === "stopBackupServe") reactor.dispatchEvent("stop_backup_serve", data.backupMsg.snfloID) + + else if(data.backupMsg.type === "startBackupStore") + reactor.dispatchEvent("start_backup_store", data.backupMsg.snfloID) + + else if(data.backupMsg.type === "stopBackupStore") + reactor.dispatchEvent("stop_backup_store", data.backupMsg.snfloID) + else if(data.backupMsg.type === "dataRequest" && floGlobals.backupStoredList.includes(data.backupMsg.snfloID)) reactor.dispatchEvent("send_stored_backup", data) } @@ -6303,12 +6347,13 @@ reactor.registerEvent("request_data"); reactor.addEventListener("request_data", function (event) { console.log("request_data"); + //request the backup data try{ var holderWS = new WebSocket("wss://" + floGlobals.supernodes[event.holder].uri + "/ws") holderWS.onopen = (evt) => { var promises = [] for(var i=0; i < floGlobals.storageList.length; i++) - promises[i] = compactIDB.searchData(floGlobals.storageList[i],{lastOnly: true},`SN_${event.floID}`) + promises[i] = compactIDB.searchData(floGlobals.storageList[i], {lastOnly: true},`SN_${event.floID}`) Promise.all(promises).then(results => { var lowerKey = {} for(var i=0; i < results.length; i++) @@ -6335,21 +6380,64 @@ reactor.registerEvent("store_backup_data"); reactor.addEventListener("store_backup_data", function (data) { console.log("store_backup_data"); + //store received backup data compactIDB.addData(floGlobals.storageList.includes(data.value.application) ? data.value.application:floGlobals.defaultStorage , data.value ,data.key,`SN_${data.snfloID}`) }) reactor.registerEvent("start_backup_serve"); - reactor.addEventListener("start_backup_serve", function (floID) { - console.log("start_backup_serve :"+floID); - floGlobals.serveList.push(floID) + reactor.addEventListener("start_backup_serve", function (snfloID) { + console.log("start_backup_serve :"+snfloID); + //start serving the dead node + if(!floGlobals.serveList.includes(snfloID)) + floGlobals.serveList.push(snfloID) + //indicate the last backup node to store the dead's backup too + var sendData = { + from: myFloID, + backupMsg: { + type: "startBackupStore", + snfloID: snfloID, + time: Date.now() + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + var lastIndex = floGlobals.backupNodes.length - 1 + floGlobals.backupNodes[lastIndex].wsConn.send(JSON.stringify(sendData)) }) reactor.registerEvent("stop_backup_serve"); - reactor.addEventListener("stop_backup_serve", function (floID) { - console.log("stop_backup_serve :"+floID); - floGlobals.serveList = floGlobals.serveList.filter(e => e !== floID) + reactor.addEventListener("stop_backup_serve", function (snfloID) { + console.log("stop_backup_serve :"+snfloID); + //stop serving the revived node + var index = floGlobals.serveList.indexOf(snfloID); + if (index !== -1) floGlobals.serveList.splice(index, 1); + //indicate the last backup node to stop storing the revived's backup + var sendData = { + from: myFloID, + backupMsg: { + type: "stopBackupStore", + snfloID: snfloID, + time: Date.now() + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + var lastIndex = floGlobals.backupNodes.length - 1 + floGlobals.backupNodes[lastIndex].wsConn.send(JSON.stringify(sendData)) }) + reactor.registerEvent("start_backup_store"); + reactor.addEventListener("start_backup_store", function (floID) { + console.log("start_backup_store :"+floID); + if(!floGlobals.backupStoredList.includes(floID)) + floGlobals.backupStoredList.push(floID) + + }) + + reactor.registerEvent("stop_backup_store"); + reactor.addEventListener("stop_backup_store", function (floID) { + console.log("stop_backup_store :"+floID); + var index = floGlobals.backupStoredList.indexOf(floID); + if (index !== -1) floGlobals.backupStoredList.splice(index, 1); + }) From b56f90d29264d5227cfa23be03bcf70fbdf7d118 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Thu, 14 Nov 2019 02:12:16 +0530 Subject: [PATCH 07/30] Flow when a node revives Added the indications and processes when a noded comes back online: Connect to the backup nodes Inform the backup nodes that node is revived Serving node transfers the serve list to revived node Storing nodes informs their farest stored backup to connect to revived node --- index.html | 255 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 174 insertions(+), 81 deletions(-) diff --git a/index.html b/index.html index 3f9eb1c..d72dddb 100644 --- a/index.html +++ b/index.html @@ -5406,7 +5406,7 @@ requestor = request[0]; request = JSON.parse(request[1]); floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { - if(floGlobals.serveList.includes(result[0].floID)){ + if((myFloID === result[0].floID || floGlobals.serveList.includes(result[0].floID))){ var filterOptions = { lowerKey: request.lowerVectorClock, upperKey: request.upperVectorClock, @@ -5432,7 +5432,7 @@ data = JSON.parse(data) if(!data.backupMsg){ //Serving Users floSupernode.kBucket.determineClosestSupernode(data.receiverID).then(result => { - if (floGlobals.serveList.includes(result[0].floID) + if ((myFloID === result[0].floID || floGlobals.serveList.includes(result[0].floID)) && data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && floCrypto.verifySign(JSON.stringify(data.message), data.sign, data.pubKey)){ var key = `${Date.now()}_${data.senderID}` @@ -6046,8 +6046,7 @@ console.log(result) connectToAllBackupSupernode().then(result => { console.log(result) - reactor.dispatchEvent("indicate_sn_up",myFloID) - reactor.dispatchEvent("request_data",{floid:myFloID, holder:floGlobals.backupNode.floID}) + reactor.dispatchEvent("supernode_back_online",myFloID) }) }) }) @@ -6260,16 +6259,39 @@ floGlobals.backupNode[i].wsConn.send(sendData) }) - reactor.registerEvent("indicate_sn_up"); - reactor.addEventListener("indicate_sn_up", function (snfloID) { - console.log("indicate_sn_up"); - //send message to backup's backup to server for backup node (*to be rectified*) - var backupMsg = { - type: "supernodeUp", - snfloID: snfloID, - time: Date.now() + reactor.registerEvent("send_message_to_node"); + reactor.addEventListener("send_message_to_node", function (event) { + console.log("send_message_to_node"); + //send message to a supernode + try{ + var nodeWS = new WebSocket("wss://" + floGlobals.supernodes[event.snfloID].uri + "/ws") + nodeWS.onopen = (evt) => { + if(event.backupMsg){ + var sendData = { + from: myFloID, + backupMsg: event.backupMsg, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + nodeWS.send(JSON.stringify(this.sendData)) + } + if(event.bulkBackupMsg){ + for(var i=0; i < event.bulkBackupMsg.length; i++){ + var sendData = { + from: myFloID, + backupMsg: event.bulkBackupMsg[i], + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + nodeWS.send(JSON.stringify(this.sendData)) + } + } + + } + nodeWS.onmessage = (evt) => console.log(evt.data); + nodeWS.onclose = (evt) => console.log("Disconnected from " + event.holder); + nodeWS.onerror = (evt) => console.log("Error connecting to " + event.holder); + }catch(error){ + console.log(error.message) } - reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg) }) reactor.registerEvent("send_backup"); @@ -6289,32 +6311,34 @@ reactor.addEventListener("send_stored_backup", function (data) { console.log("send_stored_backup"); //send stored backuped data to the requestor node - try{ - var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[data.from].uri + "/ws") - requestorWS.onopen = (evt) => { - floGlobals.storageList.forEach(obs => { - compactIDB.searchData(obs, {lowerKey: data.backupMsg.lowerKey[obs]}, `SN_${data.backupMsg.snfloID}`).then(result => { - for(k in result){ - var sendData = { - from: myFloID, - backupMsg: { - type: "backupData", - snfloID: data.backupMsg.snfloID, - key: k, - value: result[k] - }, - sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + if(data.backupMsg.snfloID === myFloID || floGlobals.backupStoredList.includes(data.backupMsg.snfloID)){ + try{ + var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[data.from].uri + "/ws") + requestorWS.onopen = (evt) => { + floGlobals.storageList.forEach(obs => { + compactIDB.searchData(obs, {lowerKey: data.backupMsg.lowerKey[obs]}, `SN_${data.backupMsg.snfloID}`).then(result => { + for(k in result){ + var sendData = { + from: myFloID, + backupMsg: { + type: "backupData", + snfloID: data.backupMsg.snfloID, + key: k, + value: result[k] + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + requestorWS.send(JSON.stringify(sendData)) } - requestorWS.send(JSON.stringify(sendData)) - } - }).catch(error => console.log(error)) - }) + }).catch(error => console.log(error)) + }) + } + requestorWS.onmessage = (evt) => console.log(evt.data); + requestorWS.onclose = (evt) => console.log("Disconnected from " + data.from); + requestorWS.onerror = (evt) => console.log("Error connecting to " + data.from); + }catch(error){ + console.log(error.message) } - requestorWS.onmessage = (evt) => console.log(evt.data); - requestorWS.onclose = (evt) => console.log("Disconnected from " + data.from); - requestorWS.onerror = (evt) => console.log("Error connecting to " + data.from); - }catch(error){ - console.log(error.message) } }) @@ -6324,23 +6348,31 @@ if(floCrypto.verifySign(JSON.stringify(data.backupMsg), data.sign, floGlobals.supernodes[data.from].pubKey)){ //Backup event messages (most crucial part) - if(data.backupMsg.type === "backupData" && floGlobals.backupStoredList.includes(data.backupMsg.snfloID)) - reactor.dispatchEvent("store_backup_data", data.backupMsg) - - else if(data.backupMsg.type === "startBackupServe") - reactor.dispatchEvent("start_backup_serve", data.backupMsg.snfloID) - - else if(data.backupMsg.type === "stopBackupServe") - reactor.dispatchEvent("stop_backup_serve", data.backupMsg.snfloID) - - else if(data.backupMsg.type === "startBackupStore") - reactor.dispatchEvent("start_backup_store", data.backupMsg.snfloID) - - else if(data.backupMsg.type === "stopBackupStore") - reactor.dispatchEvent("stop_backup_store", data.backupMsg.snfloID) - - else if(data.backupMsg.type === "dataRequest" && floGlobals.backupStoredList.includes(data.backupMsg.snfloID)) - reactor.dispatchEvent("send_stored_backup", data) + switch(data.backupMsg.type){ + case "backupData": + reactor.dispatchEvent("store_backup_data", data.backupMsg) + break; + case "supernodeUp": + reactor.dispatchEvent("supernode_back_online", data.backupMsg.snfloID) + break; + case "startBackupServe": + reactor.dispatchEvent("start_backup_serve", data.backupMsg.snfloID) + break; + case "stopBackupServe": + reactor.dispatchEvent("stop_backup_serve", data.backupMsg.snfloID) + break; + case "startBackupStore": + reactor.dispatchEvent("start_backup_store", data.backupMsg.snfloID) + break; + case "stopBackupStore": + reactor.dispatchEvent("stop_backup_store", data.backupMsg.snfloID) + break; + case "dataRequest": + reactor.dispatchEvent("send_stored_backup", data) + break; + default: + console.log(data.backupMsg) + } } }) @@ -6348,42 +6380,103 @@ reactor.addEventListener("request_data", function (event) { console.log("request_data"); //request the backup data - try{ - var holderWS = new WebSocket("wss://" + floGlobals.supernodes[event.holder].uri + "/ws") - holderWS.onopen = (evt) => { - var promises = [] - for(var i=0; i < floGlobals.storageList.length; i++) - promises[i] = compactIDB.searchData(floGlobals.storageList[i], {lastOnly: true},`SN_${event.floID}`) - Promise.all(promises).then(results => { - var lowerKey = {} - for(var i=0; i < results.length; i++) - for(key in results[i]) - lowerKey[floGlobals.storageList[i]] = key - var sendData = { - from: myFloID, - backupMsg = { - snfloID: event.floID, - lowerKey: lowerKey - }, - sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) - } - }) + var promises = [] + for(var i=0; i < floGlobals.storageList.length; i++) + promises[i] = compactIDB.searchData(floGlobals.storageList[i], {lastOnly: true},`SN_${event.floID}`) + Promise.all(promises).then(results => { + var lowerKey = {} + for(var i=0; i < results.length; i++) + for(key in results[i]) + lowerKey[floGlobals.storageList[i]] = key + var backupMsg = { + type: "dataRequest", + snfloID: event.floID, + lowerKey: lowerKey, + time: Date.now() } - holderWS.onmessage = (evt) => console.log(evt.data); - holderWS.onclose = (evt) => console.log("Disconnected from " + event.holder); - holderWS.onerror = (evt) => console.log("Error connecting to " + event.holder); - }catch(error){ - console.log(error.message) - } + reactor.dispatchEvent("send_message_to_node", {snfloID:event.holder, backupMsg:backupMsg}) + }) }) reactor.registerEvent("store_backup_data"); reactor.addEventListener("store_backup_data", function (data) { console.log("store_backup_data"); //store received backup data - compactIDB.addData(floGlobals.storageList.includes(data.value.application) ? data.value.application:floGlobals.defaultStorage , data.value ,data.key,`SN_${data.snfloID}`) + if(data.backupMsg.snfloID === myFloID || floGlobals.backupStoredList.includes(data.backupMsg.snfloID)){ + compactIDB.addData( + floGlobals.storageList.includes(data.value.application) ? data.value.application:floGlobals.defaultStorage, + data.value, data.key, `SN_${data.snfloID}` + ) + } }) + reactor.registerEvent("indicate_supernode_up"); + reactor.addEventListener("indicate_supernode_up", function (snfloID) { + console.log("indicate_supernode_up"); + //send message to backup's backup to server for backup node (*to be rectified*) + var backupMsg = { + type: "supernodeUp", + snfloID: snfloID, + time: Date.now() + } + reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg) + }) + + reactor.registerEvent("supernode_back_online"); + reactor.addEventListener("supernode_back_online", function (snfloID) { + console.log("supernode_back_online"); + if(floGlobals.serveList.includes(snfloID)){ + //inform the revived node to serve the other dead nodes + var kBucketArray = floSupernode.kBucket.supernodeKBucket.toArray + for(var i=0; i < floGlobals.serveList.length; i++) + if(kBucketArray.indexOf(floGlobals.serveList[i]) <= kBucketArray.indexOf(snfloID)){ + var backupMsg = { + type: "startBackupServe", + snfloID: floGlobals.serveList[i], + time: Date.now() + } + reactor.dispatchEvent("stop_backup_serve", floGlobals.serveList[i]) + reactor.dispatchEvent("send_message_to_node", {snfloID:snfloID, backupMsg:backupMsg}) + i--; //reduce iterator as an element is removed + } + } + if(floGlobals.backupStoredList.includes(snfloID)){ + var lastBackup = floGlobals.backupStoredList.pop() + //inform the revived node to store the backup + var backupMsg1 = { + type: "startBackupStore", + snfloID:lastBackup, + time: Date.now() + } + reactor.dispatchEvent("send_message_to_node", {snfloID:snfloID, backupMsg:backupMsg1}) + //inform the backup node that a node is revived + var backupMsg2 = { + type: "supernodeUp", + snfloID:snfloID, + time: Date.now() + } + reactor.dispatchEvent("send_message_to_node", {snfloID:lastBackup, backupMsg:backupMsg2}) + } else { + //connect to the revived node as backup if needed + var kBucketArray = floSupernode.kBucket.supernodeKBucket.toArray + var index = false + for(var i = 0; i < floGlobals.backupNodes.length; i++){ + if(kBucketArray.indexOf(floGlobals.backupNodes[i].floID) == kBucketArray.indexOf(snfloID)) //revived node is already connected + break; + else if(kBucketArray.indexOf(floGlobals.backupNodes[i].floID) > kBucketArray.indexOf(snfloID)){ + index = i; + break; + } + } + if(index){ + initateBackupWebsocket(snfloID).then(result => { + floGlobals.backupNodes.splice(index,0,result) // add revived node as backup node + floGlobals.backupNodes.pop() // remove the last extra backup node + }).catch(error => console.log(error)) + } + } + }) + reactor.registerEvent("start_backup_serve"); reactor.addEventListener("start_backup_serve", function (snfloID) { console.log("start_backup_serve :"+snfloID); From f123cf611d310c12eef77a2c00b8c74faaab8dc5 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 15 Nov 2019 17:30:23 +0530 Subject: [PATCH 08/30] Reforming and invoke request data Reformed the request data event Trigger the request data event when a node comes back online and when a node is asked to store backup of a node --- index.html | 39 +++++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/index.html b/index.html index d72dddb..406bd6d 100644 --- a/index.html +++ b/index.html @@ -6046,7 +6046,7 @@ console.log(result) connectToAllBackupSupernode().then(result => { console.log(result) - reactor.dispatchEvent("supernode_back_online",myFloID) + reactor.dispatchEvent("indicate_supernode_up",myFloID) }) }) }) @@ -6308,21 +6308,21 @@ }) reactor.registerEvent("send_stored_backup"); - reactor.addEventListener("send_stored_backup", function (data) { + reactor.addEventListener("send_stored_backup", function (event) { console.log("send_stored_backup"); //send stored backuped data to the requestor node - if(data.backupMsg.snfloID === myFloID || floGlobals.backupStoredList.includes(data.backupMsg.snfloID)){ + if(event.snfloID === myFloID || floGlobals.backupStoredList.includes(event.snfloID)){ try{ - var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[data.from].uri + "/ws") + var requestorWS = new WebSocket("wss://" + floGlobals.supernodes[event.from].uri + "/ws") requestorWS.onopen = (evt) => { floGlobals.storageList.forEach(obs => { - compactIDB.searchData(obs, {lowerKey: data.backupMsg.lowerKey[obs]}, `SN_${data.backupMsg.snfloID}`).then(result => { + compactIDB.searchData(obs, {lowerKey: event.lowerKey[obs]}, `SN_${data.backupMsg.snfloID}`).then(result => { for(k in result){ var sendData = { from: myFloID, backupMsg: { type: "backupData", - snfloID: data.backupMsg.snfloID, + snfloID: event.snfloID, key: k, value: result[k] }, @@ -6334,8 +6334,8 @@ }) } requestorWS.onmessage = (evt) => console.log(evt.data); - requestorWS.onclose = (evt) => console.log("Disconnected from " + data.from); - requestorWS.onerror = (evt) => console.log("Error connecting to " + data.from); + requestorWS.onclose = (evt) => console.log("Disconnected from " + event.from); + requestorWS.onerror = (evt) => console.log("Error connecting to " + event.from); }catch(error){ console.log(error.message) } @@ -6362,13 +6362,13 @@ reactor.dispatchEvent("stop_backup_serve", data.backupMsg.snfloID) break; case "startBackupStore": - reactor.dispatchEvent("start_backup_store", data.backupMsg.snfloID) + reactor.dispatchEvent("start_backup_store", {from:data.from, snfloID:data.backupMsg.snfloID}) break; case "stopBackupStore": reactor.dispatchEvent("stop_backup_store", data.backupMsg.snfloID) break; case "dataRequest": - reactor.dispatchEvent("send_stored_backup", data) + reactor.dispatchEvent("send_stored_backup", {from:data.from, snfloID:data.backupMsg.snfloID, lowerKey:data.backupMsg.lowerKey}) break; default: console.log(data.backupMsg) @@ -6382,7 +6382,7 @@ //request the backup data var promises = [] for(var i=0; i < floGlobals.storageList.length; i++) - promises[i] = compactIDB.searchData(floGlobals.storageList[i], {lastOnly: true},`SN_${event.floID}`) + promises[i] = compactIDB.searchData(floGlobals.storageList[i], {lastOnly: true},`SN_${event.snfloID}`) Promise.all(promises).then(results => { var lowerKey = {} for(var i=0; i < results.length; i++) @@ -6390,7 +6390,7 @@ lowerKey[floGlobals.storageList[i]] = key var backupMsg = { type: "dataRequest", - snfloID: event.floID, + snfloID: event.snfloID, lowerKey: lowerKey, time: Date.now() } @@ -6413,13 +6413,14 @@ reactor.registerEvent("indicate_supernode_up"); reactor.addEventListener("indicate_supernode_up", function (snfloID) { console.log("indicate_supernode_up"); - //send message to backup's backup to server for backup node (*to be rectified*) + //send message to backup's backup to server for backup node var backupMsg = { type: "supernodeUp", snfloID: snfloID, time: Date.now() } reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg) + reactor.dispatchEvent("request_data", {holder:floGlobals.backupNodes[0].floID, snfloID:myFloID}) }) reactor.registerEvent("supernode_back_online"); @@ -6518,11 +6519,13 @@ }) reactor.registerEvent("start_backup_store"); - reactor.addEventListener("start_backup_store", function (floID) { - console.log("start_backup_store :"+floID); - if(!floGlobals.backupStoredList.includes(floID)) - floGlobals.backupStoredList.push(floID) - + reactor.addEventListener("start_backup_store", function (event) { + console.log("start_backup_store :"+event.snfloID); + if(!floGlobals.backupStoredList.includes(event.snfloID)) + floGlobals.backupStoredList.push(event.snfloID) + initIndexedDBforSupernodeDataStorage(event.snfloID).then(result => { + reactor.dispatchEvent("request_data",{holder:event.from, snfloID:event.snfloID}) + }).catch(error => console.log(error)) }) reactor.registerEvent("stop_backup_store"); From 466d0d621b8ce5bef7e14733c609013317b64463 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 15 Nov 2019 17:56:04 +0530 Subject: [PATCH 09/30] bug fix : start serve and store backup process only when its not --- index.html | 48 ++++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/index.html b/index.html index 406bd6d..7c87592 100644 --- a/index.html +++ b/index.html @@ -6414,13 +6414,19 @@ reactor.addEventListener("indicate_supernode_up", function (snfloID) { console.log("indicate_supernode_up"); //send message to backup's backup to server for backup node - var backupMsg = { + var backupMsg1 = { type: "supernodeUp", snfloID: snfloID, time: Date.now() } - reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg) - reactor.dispatchEvent("request_data", {holder:floGlobals.backupNodes[0].floID, snfloID:myFloID}) + reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg1) + var backupMsg2 = { + type: "startBackupStore", + snfloID: snfloID, + time: Date.now() + } + reactor.dispatchEvent("send_message_to_backup_nodes", backupMsg2) + reactor.dispatchEvent("request_data", {holder:floGlobals.backupNodes[0].floID, snfloID:snfloID}) }) reactor.registerEvent("supernode_back_online"); @@ -6471,7 +6477,7 @@ } if(index){ initateBackupWebsocket(snfloID).then(result => { - floGlobals.backupNodes.splice(index,0,result) // add revived node as backup node + floGlobals.backupNodes.splice(index, 0, result) // add revived node as backup node floGlobals.backupNodes.pop() // remove the last extra backup node }).catch(error => console.log(error)) } @@ -6482,20 +6488,21 @@ reactor.addEventListener("start_backup_serve", function (snfloID) { console.log("start_backup_serve :"+snfloID); //start serving the dead node - if(!floGlobals.serveList.includes(snfloID)) + if(!floGlobals.serveList.includes(snfloID)){ floGlobals.serveList.push(snfloID) - //indicate the last backup node to store the dead's backup too - var sendData = { - from: myFloID, - backupMsg: { - type: "startBackupStore", - snfloID: snfloID, - time: Date.now() - }, - sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + //indicate the last backup node to store the dead's backup too + var sendData = { + from: myFloID, + backupMsg: { + type: "startBackupStore", + snfloID: snfloID, + time: Date.now() + }, + sign: floCrypto.signData(JSON.stringify(this.backupMsg), myPrivKey) + } + var lastIndex = floGlobals.backupNodes.length - 1 + floGlobals.backupNodes[lastIndex].wsConn.send(JSON.stringify(sendData)) } - var lastIndex = floGlobals.backupNodes.length - 1 - floGlobals.backupNodes[lastIndex].wsConn.send(JSON.stringify(sendData)) }) reactor.registerEvent("stop_backup_serve"); @@ -6521,11 +6528,12 @@ reactor.registerEvent("start_backup_store"); reactor.addEventListener("start_backup_store", function (event) { console.log("start_backup_store :"+event.snfloID); - if(!floGlobals.backupStoredList.includes(event.snfloID)) + if(!floGlobals.backupStoredList.includes(event.snfloID)){ floGlobals.backupStoredList.push(event.snfloID) - initIndexedDBforSupernodeDataStorage(event.snfloID).then(result => { - reactor.dispatchEvent("request_data",{holder:event.from, snfloID:event.snfloID}) - }).catch(error => console.log(error)) + initIndexedDBforSupernodeDataStorage(event.snfloID).then(result => { + reactor.dispatchEvent("request_data",{holder:event.from, snfloID:event.snfloID}) + }).catch(error => console.log(error)) + } }) reactor.registerEvent("stop_backup_store"); From c6383a6f8f178102f6d98a52978a8ee6a2c0b3c3 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 16 Nov 2019 01:20:50 +0530 Subject: [PATCH 10/30] bug fix Fixed : getNextSupernode and getPrevSupernode return object bug --- index.html | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/index.html b/index.html index 7c87592..016a214 100644 --- a/index.html +++ b/index.html @@ -5238,7 +5238,7 @@ if (!isFloIdUint8) flo_addr = this.floIdToKbucketId(flo_addr); const KA = KB.toArray(); - let pos = KA.indexOf(flo_addr) + let pos = KB._indexOf(KB.root,flo_addr) var prevSupernode = [] for(var i = 0; i < n; i++){ if(pos - i < 0) @@ -5261,7 +5261,7 @@ if (!isFloIdUint8) flo_addr = this.floIdToKbucketId(flo_addr); const KA = KB.toArray(); - let pos = KA.indexOf(flo_addr) + let pos = KB._indexOf(KB.root,flo_addr) var nextSupernode = [] for(var i = 0; i < n; i++){ if(pos + i >= KA.length) @@ -6076,6 +6076,7 @@ function initIndexedDBforSupernodeDataStorage(floID){ return new Promise((resolve, reject) => { var indexesList = ["senderID","receiverID","pubKey","message","sign","application","type","comment"]; + var storageList = floGlobals.storageList var idbObj = {} for(var i=0;i { - connectToBackupSupernode(nextBackupNode).then(backupConn => { - floGlobals.backupNodes[i] = backupConn - connectToAllBackupSupernode(backupConn.floID, i+1) - .then(result => resolve(result)) - .catch(error => reject(error)) - }) - }).catch(error => console.log(error)) + if(nextBackupNode[0].floID != myFloID){ + connectToBackupSupernode(nextBackupNode[0].floID).then(backupConn => { + floGlobals.backupNodes[i] = backupConn + connectToAllBackupSupernode(backupConn.floID, i+1) + .then(result => resolve(result)) + .catch(error => reject(error)) + }) + }else + reject("No more supernodes available") + }).catch(error => reject(error)) } }) @@ -6150,7 +6154,7 @@ .catch(error => { console.log(error) floSupernode.kBucket.getNextSupernode(backupNodeID).then(nextBackupNode => { - connectToBackupSupernode(nextBackupNode).then(result => resolve(result)) + connectToBackupSupernode(nextBackupNode[0].floID).then(result => resolve(result)) }).catch(error => console.log(error)) }) }) From 37ece209fa1c1d7d9b94b4d2b351bdf18978400f Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 16 Nov 2019 19:32:46 +0530 Subject: [PATCH 11/30] adding sleep delay before connecting to backup nodes --- index.html | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.html b/index.html index 016a214..756aea2 100644 --- a/index.html +++ b/index.html @@ -6026,7 +6026,12 @@ From bfb86f60858d189b61e0070c894c320239ed79fb Mon Sep 17 00:00:00 2001 From: sairajzero Date: Tue, 26 Nov 2019 16:16:05 +0530 Subject: [PATCH 21/30] Collect application list from blockchain Supernode now collect list of applications with their adminID from blockchain data --- index.html | 91 ++++++++++++++++++++++++++++++++---------------------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/index.html b/index.html index 54b2d27..9a62c2d 100644 --- a/index.html +++ b/index.html @@ -33,6 +33,8 @@ supernodes: {}, //each supnernode must be stored as floID : {uri:,pubKey:} diskList : ["General"], defaultDisk : "General", + applicationList:{}, + appSubAdmins:{}, serveList : [], storedList : [], supernodeConfig : {}, @@ -6038,30 +6040,27 @@ initIndexedDBforSupernodeConfig().then(result => { console.log(result) - readSupernodeListFromAPI().then(result => { + refreshBlockchainData().then(result => { console.log(result) - floSupernode.kBucket.launch().then(result => { - console.log(result) - if (myFloID in floGlobals.supernodes) { - initIndexedDBforSupernodeDataStorage(myFloID).then(result => { + if (myFloID in floGlobals.supernodes) { + initIndexedDBforSupernodeDataStorage(myFloID).then(result => { + console.log(result) + serverPwd = prompt("Enter Server Pass!") + setInterval(autoRefreshBlockchainData, 3600000); + floSupernode.initSupernode(serverPwd, myFloID).then(async result => { console.log(result) - serverPwd = prompt("Enter Server Pass!") - setInterval(refreshBlockchainData, 3600000); - floSupernode.initSupernode(serverPwd, myFloID).then(async result => { + floGlobals.serveList.push(myFloID) + floGlobals.storedList.push(myFloID) + await sleep(5000) + connectToAllBackupSupernode().then(async result => { console.log(result) - floGlobals.serveList.push(myFloID) - floGlobals.storedList.push(myFloID) - await sleep(5000) - connectToAllBackupSupernode().then(async result => { - console.log(result) - await sleep(2000) - reactor.dispatchEvent("indicate_supernode_up",myFloID) - }) - }) - }) - } - }) - }) + await sleep(2000) + reactor.dispatchEvent("indicate_supernode_up",myFloID) + }).catch(error => console.log(error)) + }).catch(error => console.log(error)) + }).catch(error => console.log(error)) + } + }).catch(error => console.log(error)) }).catch(error => console.log(error)) } @@ -6073,6 +6072,10 @@ config:{}, supernodes:{ indexes:{ uri:null, pubKey:null } + }, + applications:{}, + appSubAdmins:{ + indexes:{ app:null, subAdminID:null } } } compactIDB.setDefaultDB("SupernodeConfig") @@ -6100,16 +6103,25 @@ }) } - function refreshBlockchainData(){ - readSupernodeListFromAPI().then(result => { - console.log(result) - floSupernode.kBucket.launch().then(result => { - console.log(result) - }) - }).catch(error => console.log(error)) + function autoRefreshBlockchainData(){ + refreshBlockchainData() + .then(result => console.log(result)) + .catch(error => console.log(error)) } - function readSupernodeListFromAPI(){ + function refreshBlockchainData(){ + return new Promise((resolve,reject) => { + readSupernodeDataFromAPI().then(result => { + console.log(result) + floSupernode.kBucket.launch().then(result => { + console.log(result) + resolve("Refreshed Data from blockchain") + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) + } + + function readSupernodeDataFromAPI(){ return new Promise((resolve,reject) => { compactIDB.readData("lastTx",floGlobals.adminID).then(lastTx => { floBlockchainAPI.readData(floGlobals.adminID,{ignoreOld:lastTx,sendOnly:true,pattern:"SuperNodeStorage"}).then(result => { @@ -6121,16 +6133,21 @@ compactIDB.writeData("supernodes",content.addNodes[sn],sn); for(c in content.config) compactIDB.writeData("config",content.config[c],c) + for(app in content.application) + compactIDB.writeData("applications",content.application[app],app) } compactIDB.writeData("lastTx",result.totalTxs,floGlobals.adminID); - compactIDB.readAllData("supernodes").then(result => { - floGlobals.supernodes = result - compactIDB.readAllData("config").then(result => { - floGlobals.supernodeConfig = result - resolve("Read supernode from blockchain"); - }) - }) - }) + compactIDB.readAllData("supernodes").then(supernodes => { + floGlobals.supernodes = supernodes + compactIDB.readAllData("config").then(config => { + floGlobals.supernodeConfig = config + compactIDB.readAllData("applications").then(applications => { + floGlobals.applicationList = applications + resolve("Read supernode from blockchain"); + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) }).catch(error => reject(error)) }) } From 76ec38ceedee6f06280dc87b8db8e5bb71d54b9f Mon Sep 17 00:00:00 2001 From: sairajzero Date: Tue, 26 Nov 2019 17:21:43 +0530 Subject: [PATCH 22/30] read subAdmin list for application from blockchain now reads subadmin list for every application from blockchain. subadmin list will be used to delete unwanted messages from supernode IDB --- index.html | 44 ++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/index.html b/index.html index 9a62c2d..91a89ae 100644 --- a/index.html +++ b/index.html @@ -6113,9 +6113,11 @@ return new Promise((resolve,reject) => { readSupernodeDataFromAPI().then(result => { console.log(result) - floSupernode.kBucket.launch().then(result => { - console.log(result) - resolve("Refreshed Data from blockchain") + readAppSubAdminListFromAPI().then(result => { + floSupernode.kBucket.launch().then(result => { + console.log(result) + resolve("Refreshed Data from blockchain") + }).catch(error => reject(error)) }).catch(error => reject(error)) }).catch(error => reject(error)) }) @@ -6143,7 +6145,7 @@ floGlobals.supernodeConfig = config compactIDB.readAllData("applications").then(applications => { floGlobals.applicationList = applications - resolve("Read supernode from blockchain"); + resolve("Read Supernode Data from Blockchain"); }).catch(error => reject(error)) }).catch(error => reject(error)) }).catch(error => reject(error)) @@ -6152,6 +6154,40 @@ }) } + function readAppSubAdminListFromAPI(){ + return new Promise((resolve,reject) => { + var promises = [] + for(app in floGlobals.applicationList){ + var promise = new Promise((resolve,reject) => { + compactIDB.readData("appSubAdmins", app).then(subAdmins => { + compactIDB.readData("lastTx",floGlobals.applicationList[app]).then(lastTx => { + floBlockchainAPI.readData(floGlobals.applicationList[app],{ignoreOld:lastTx,sentOnly:true,pattern:app}).then(result => { + for(var i = result.data.length-1; i>=0; i--){ + var content = JSON.parse(result.data[i])[app]; + if(Array.isArray(content.removeSubAdmin)) + subAdmins = subAdmins.filter(x => !content.removeSubAdmin.includes(x)); + if(Array.isArray(content.addSubAdmin)) + subAdmins = subAdmins.concat(content.addSubAdmin) + } + compactIDB.writeData("lastTx", result.totalTxs, floGlobals.adminID); + compactIDB.writeData("appSubAdmins", subAdmins, app) + .then(res => resolve(app)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) + promises.push(promise) + } + Promise.all(promises).then(results => { + compactIDB.readAllData("appSubAdmins").then(result => { + floGlobals.appSubAdmins = result + resolve("Read all subAdmin list from blockchain") + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) + } + function connectToAllBackupSupernode(curNode = myFloID, i = 0){ return new Promise((resolve,reject) => { if(i >= floGlobals.supernodeConfig.backupDepth) From f543c2c0d34f4c8ff12e0da4f4787544c843b804 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 27 Nov 2019 10:26:01 +0530 Subject: [PATCH 23/30] auto-delete feature Supernode automatically deletes messages that are not sent by subAdmins of an application and applications that are not associated. The delete occurs every one hour, the messages are stored for a configurable time (deleteDelay) before deleted --- index.html | 53 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/index.html b/index.html index 91a89ae..fac7914 100644 --- a/index.html +++ b/index.html @@ -26,6 +26,7 @@ FLO_TEST: 'https://testnet-flosight.duckdns.org' }, adminID: "FEzk75EGMPEQMrCuPosGiwuK162hcEu49E", + refreshDelay: 3600000, //sendAmt: 0.001, //fee: 0.0005, @@ -6046,7 +6047,7 @@ initIndexedDBforSupernodeDataStorage(myFloID).then(result => { console.log(result) serverPwd = prompt("Enter Server Pass!") - setInterval(autoRefreshBlockchainData, 3600000); + setInterval(autoRefreshBlockchainData, floGlobals.refreshDelay); floSupernode.initSupernode(serverPwd, myFloID).then(async result => { console.log(result) floGlobals.serveList.push(myFloID) @@ -6104,9 +6105,38 @@ } function autoRefreshBlockchainData(){ - refreshBlockchainData() - .then(result => console.log(result)) - .catch(error => console.log(error)) + refreshBlockchainData().then(result => { + console.log(result) + autoDeleteStoredData() + .then(result => console.log(result)) + .catch(error => console.log(error)) + }).catch(error => console.log(error)) + } + + function autoDeleteStoredData(){ + return new Promise((resolve, reject) => { + var deleteEnd = Date.now() - floGlobals.supernodeConfig.deleteDelay + var deleteStart = deleteEnd - floGlobals.refreshDelay + var promises = [] + var filterOptions = { + lowerKey: `${deleteStart}`, + upperKey: `${deleteEnd}`, + } + for(var i = 0; i < floGlobals.storedList.length; i++){ + var promise = new Promise((res,rej) => { + compactIDB.searchData(floGlobals.defaultDisk,filterOptions,`SN_${floGlobals.storedList[i]}`).then(results => { + for(key in results) + if(!floGlobals.applicationList.includes(results[key].application) || !floGlobals.appSubAdmins[results[key].application].includes(results[key].senderID)) + compactIDB.removeData(floGlobals.defaultDisk, key, `SN_${floGlobals.storedList[i]}`) + res(`Auto-delete successful for SN_${floGlobals.storedList[i]} from ${deleteStart} to ${deleteEnd}`) + }).catch(error => rej(error)) + }) + promises.push(promise) + } + Promise.all(promises).then(results => { + resolve(`Auto-delete successful from ${deleteStart} to ${deleteEnd}`) + }).catch(error => reject(error)) + }) } function refreshBlockchainData(){ @@ -6158,7 +6188,7 @@ return new Promise((resolve,reject) => { var promises = [] for(app in floGlobals.applicationList){ - var promise = new Promise((resolve,reject) => { + var promise = new Promise((res,rej) => { compactIDB.readData("appSubAdmins", app).then(subAdmins => { compactIDB.readData("lastTx",floGlobals.applicationList[app]).then(lastTx => { floBlockchainAPI.readData(floGlobals.applicationList[app],{ignoreOld:lastTx,sentOnly:true,pattern:app}).then(result => { @@ -6171,11 +6201,11 @@ } compactIDB.writeData("lastTx", result.totalTxs, floGlobals.adminID); compactIDB.writeData("appSubAdmins", subAdmins, app) - .then(res => resolve(app)) - .catch(error => reject(error)) - }).catch(error => reject(error)) - }).catch(error => reject(error)) - }).catch(error => reject(error)) + .then(result => res(app)) + .catch(error => rej(error)) + }).catch(error => rej(error)) + }).catch(error => rej(error)) + }).catch(error => rej(error)) }) promises.push(promise) } @@ -6202,11 +6232,10 @@ connectToAllBackupSupernode(backupConn.floID, i+1) .then(result => resolve(result)) .catch(error => reject(error)) - }) + }).catch(error => reject(error)) } }).catch(error => reject(error)) } - }) } From 1d845a1ca4ddbd0c8b56d36d41033bb3cd09f869 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sun, 1 Dec 2019 03:09:43 +0530 Subject: [PATCH 24/30] store/retrive credentials securely for automatic login Adding Sharmirs Secret functions Store and retrive the private key and server password securely A dump credential for each credential will be stored to improve security clearCredentials(): clear the stored credentials --- index.html | 781 +++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 762 insertions(+), 19 deletions(-) diff --git a/index.html b/index.html index fac7914..2ba0dad 100644 --- a/index.html +++ b/index.html @@ -47,6 +47,8 @@

SuperNode Storage

@@ -6035,35 +6659,153 @@ } function onLoadStartUp() { - myPrivKey = prompt("Enter Private Key : ") - myPubKey = floCrypto.getPubKeyHex(myPrivKey) - myFloID = floCrypto.getFloIDfromPubkeyHex(myPubKey) initIndexedDBforSupernodeConfig().then(result => { console.log(result) refreshBlockchainData().then(result => { console.log(result) - if (myFloID in floGlobals.supernodes) { - initIndexedDBforSupernodeDataStorage(myFloID).then(result => { - console.log(result) - serverPwd = prompt("Enter Server Pass!") - setInterval(autoRefreshBlockchainData, floGlobals.refreshDelay); - floSupernode.initSupernode(serverPwd, myFloID).then(async result => { + getCredentials().then(result => { + if (myFloID in floGlobals.supernodes) { + initIndexedDBforSupernodeDataStorage(myFloID).then(result => { console.log(result) - floGlobals.serveList.push(myFloID) - floGlobals.storedList.push(myFloID) - await sleep(5000) - connectToAllBackupSupernode().then(async result => { + setInterval(autoRefreshBlockchainData, floGlobals.refreshDelay); + floSupernode.initSupernode(serverPwd, myFloID).then(async result => { console.log(result) - await sleep(2000) - reactor.dispatchEvent("indicate_supernode_up",myFloID) + floGlobals.serveList.push(myFloID) + floGlobals.storedList.push(myFloID) + await sleep(5000) + connectToAllBackupSupernode().then(async result => { + console.log(result) + await sleep(2000) + reactor.dispatchEvent("indicate_supernode_up",myFloID) + }).catch(error => console.log(error)) }).catch(error => console.log(error)) }).catch(error => console.log(error)) - }).catch(error => console.log(error)) - } + } + }).catch(error => console.log(error)) }).catch(error => console.log(error)) }).catch(error => console.log(error)) } + + function getCredentials(){ + return new Promise((resolve, reject) => { + getPrivateKeyCredentials().then(privKey => { + myPrivKey = privKey + myPubKey = floCrypto.getPubKeyHex(myPrivKey) + myFloID = floCrypto.getFloIDfromPubkeyHex(myPubKey) + getServerPasswordCredentials().then(pass => { + serverPwd = pass + resolve('Login Credentials loaded successful') + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) + } + + function getPrivateKeyCredentials(){ + return new Promise((resolve, reject) => { + var indexArr = localStorage.getItem("privKey") + if(indexArr){ + readSharesFromIDB(JSON.parse(indexArr)) + .then(result => resolve(result)) + .catch(error => reject(error)) + }else{ + try{ + var privKey = prompt("Enter Private Key: ") + if(!privKey) + return reject("Empty Private Key") + var floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex(privKey)) + console.log(floID) + alert(`Supernode floID: ${floID}`) + }catch(error){ + console.log(error) + return reject("Invalid Private Key") + } + var threshold = floCrypto.randInt(10,20) + writeSharesToIDB(floCrypto.createShamirsSecretShares(privKey, threshold, threshold)).then(resultIndexes =>{ + //store index keys in localStorage + localStorage.setItem("privKey", JSON.stringify(resultIndexes)) + //also add a dummy privatekey to the IDB + var randomPrivKey = floCrypto.generateNewID().privKey + var randomThreshold = floCrypto.randInt(10,20) + writeSharesToIDB(floCrypto.createShamirsSecretShares(randomPrivKey, randomThreshold, randomThreshold)) + //resolve private Key + resolve(privKey) + }).catch(error => reject(error)) + } + }) + } + + function getServerPasswordCredentials(){ + return new Promise((resolve, reject) => { + var indexArr = localStorage.getItem("serverPass") + if(indexArr){ + readSharesFromIDB(JSON.parse(indexArr)) + .then(result => resolve(result)) + .catch(error => reject(error)) + }else{ + var serverPass = prompt("Enter Server Password: ") + if(!serverPass) + return reject("Empty Server Password") + var threshold = floCrypto.randInt(10,20) + writeSharesToIDB(floCrypto.createShamirsSecretShares(serverPass, threshold, threshold)).then(resultIndexes =>{ + //store index keys in localStorage + localStorage.setItem("serverPass", JSON.stringify(resultIndexes)) + //also add a dummy string to the IDB + var randomLength = floCrypto.randInt(serverPass.length - 5, serverPass.length + 5) + var randomString = floCrypto.randString(randomLength) + var randomThreshold = floCrypto.randInt(10,20) + writeSharesToIDB(floCrypto.createShamirsSecretShares(randomPrivKey, randomThreshold, randomThreshold)) + //resolve private Key + resolve(serverPass) + }).catch(error => reject(error)) + } + }) + } + + function readSharesFromIDB(indexArr){ + return new Promise((resolve, reject) => { + var promises = [] + for(var i = 0; i < indexArr.length; i++) + promises.push(compactIDB.readData('credentials', indexArr[i])) + Promise.all(promises).then(shares => { + var secret = floCrypto.retrieveShamirSecret(shares) + if(secret) + resolve(secret) + else + reject("Shares are insufficient or incorrect") + }).catch(error => reject(error)) + }) + } + + function writeSharesToIDB(shares, i = 0, resultIndexes = []){ + return new Promise((resolve, reject) => { + if(i >= shares.length) + return resolve(resultIndexes) + var n = floCrypto.randInt(0, 100000) + compactIDB.addData("credentials", shares[i], n).then(res => { + resultIndexes.push(n) + writeSharesToIDB(shares, i+1, resultIndexes) + .then(result => resolve(resolve)) + }).catch(error => { + writeSharesToIDB(shares, i, resultIndexes) + .then(result => resolve(resolve)) + }) + }) + } + + function clearCredentials(type = 'Both'){ + if(type === 'Both') + return clearCredentials('privKey') + clearCredentials('serverPass') + else if(type === 'privKey' || type === 'serverPass'){ + var indexArr = localStorage.getItem(type) + if(!indexArr) + return `${type} credentials not found!\n` + indexArr = JSON.parse(indexArr) + indexArr.forEach(i => compactIDB.removeData('credentials', indexArr[i])) + localStorage.removeItem(type) + return `${type} credentials deleted!\n` + } + } function initIndexedDBforSupernodeConfig(){ return new Promise((resolve, reject) => { @@ -6071,6 +6813,7 @@ var snObj = { lastTx:{}, config:{}, + credentials:{}, supernodes:{ indexes:{ uri:null, pubKey:null } }, @@ -6079,8 +6822,8 @@ indexes:{ app:null, subAdminID:null } } } - compactIDB.setDefaultDB("SupernodeConfig") - compactIDB.initDB("SupernodeConfig", snObj) + compactIDB.setDefaultDB("SupernodeUtil") + compactIDB.initDB("SupernodeUtil", snObj) .then(result => resolve("Initiated supernode configuration IDB")) .catch(error => reject(error)); }) From 5f7737ea27cefc508b613d31e1fc983019b4b62d Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sun, 1 Dec 2019 11:40:19 +0530 Subject: [PATCH 25/30] adding multifetch for blockchain API --- index.html | 125 +++++++++++++++++++++++++++++------------------------ 1 file changed, 68 insertions(+), 57 deletions(-) diff --git a/index.html b/index.html index 2ba0dad..7de6c92 100644 --- a/index.html +++ b/index.html @@ -22,8 +22,8 @@ //Required for blockchain API operators apiURL: { - FLO: 'https://livenet.flocha.in', - FLO_TEST: 'https://testnet-flosight.duckdns.org' + FLO: ['https://explorer.mediciland.com/', 'https://livenet.flocha.in/', 'https://flosight.duckdns.org/', 'http://livenet-explorer.floexperiments.com', 'http://ec2-13-233-133-128.ap-south-1.compute.amazonaws.com/', 'http://ec2-13-233-131-136.ap-south-1.compute.amazonaws.com/', 'http://ec2-13-233-194-1.ap-south-1.compute.amazonaws.com/'], + FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/'] }, adminID: "FEzk75EGMPEQMrCuPosGiwuK162hcEu49E", refreshDelay: 3600000, @@ -5615,42 +5615,66 @@ /* FLO Blockchain Operator to send/receive data from blockchain using API calls*/ const floBlockchainAPI = { - //Promised AJAX function to get data from API - promisedAJAX: function (method, uri) { + util: { + serverList: floGlobals.apiURL[floGlobals.blockchain].slice(0), + curPos: floCrypto.randInt(0, floGlobals.apiURL[floGlobals.blockchain].length), + fetch_retry: function(apicall){ + return new Promise((resolve,reject) => { + this.serverList.splice(this.curPos, 1); + this.curPos = floCrypto.randInt(0, this.serverList.length) + this.fetch_api(apicall) + .then(result => resolve(result)) + .catch(error => reject(error)); + }) + }, + fetch_api: function(apicall){ + return new Promise((resolve, reject) => { + if(this.serverList.length === 0) + reject("No floSight server working") + else{ + fetch(this.serverList[this.curPos] + apicall).then(response => { + if(response.ok) + response.json().then(data => resolve(data)); + else{ + this.fetch_retry(apicall) + .then(result => resolve(result)) + .catch(error => reject(error)); + } + }).catch(error => { + this.fetch_retry(apicall) + .then(result => resolve(result)) + .catch(error => reject(error)); + }) + } + }) + } + }, + + //Promised function to get data from API + promisedAPI: function (apicall) { return new Promise((resolve, reject) => { - var request = new XMLHttpRequest(); - var url = `${floGlobals.apiURL[floGlobals.blockchain]}/${uri}`; - console.log(url) - request.open(method, url, true); - request.onload = (evt) => { - if (request.readyState == 4 && request.status == 200) - resolve(request.response); - else - reject(request.response); - }; - request.send(); + console.log(apicall) + this.util.fetch_api(apicall) + .then(result => resolve(result)) + .catch(error => reject(error)); }); }, //Get balance for the given Address getBalance: function (addr) { return new Promise((resolve, reject) => { - this.promisedAJAX("GET", `api/addr/${addr}/balance`).then(balance => { - resolve(parseFloat(balance)); - }).catch(error => { - reject(error); - }); + this.promisedAPI(`api/addr/${addr}/balance`) + .then(balance => resolve(parseFloat(balance))) + .catch(error => reject(error)); }); }, //Write Data into blockchain writeData: function (senderAddr, Data, PrivKey, receiverAddr = floGlobals.adminID) { return new Promise((resolve, reject) => { - this.sendTx(senderAddr, receiverAddr, floGlobals.sendAmt, PrivKey, Data).then(txid => { - resolve(txid); - }).catch(error => { - reject(error); - }); + this.sendTx(senderAddr, receiverAddr, floGlobals.sendAmt, PrivKey, Data) + .then(txid => resolve(txid)) + .catch(error => reject(error)) }); }, @@ -5669,8 +5693,7 @@ var trx = bitjs.transaction(); var utxoAmt = 0.0; var fee = floGlobals.fee; - this.promisedAJAX("GET", `api/addr/${senderAddr}/utxo`).then(response => { - var utxos = JSON.parse(response); + this.promisedAPI(`api/addr/${senderAddr}/utxo`).then(utxos => { for (var i = utxos.length - 1; (i >= 0) && (utxoAmt < sendAmt + fee); i--) { if (utxos[i].confirmations) { @@ -5687,15 +5710,11 @@ trx.addoutput(senderAddr, change); trx.addflodata(floData); var signedTxHash = trx.sign(PrivKey, 1); - this.broadcastTx(signedTxHash).then(txid => { - resolve(txid) - }).catch(error => { - reject(error); - }); + this.broadcastTx(signedTxHash) + .then(txid => resolve(txid)) + .catch(error => reject(error)) } - }).catch(error => { - reject(error); - }); + }).catch(error => reject(error)) } }); }, @@ -5704,7 +5723,7 @@ broadcastTx: function (signedTxHash) { return new Promise((resolve, reject) => { var request = new XMLHttpRequest(); - var url = `${floGlobals.apiURL[floGlobals.blockchain]}/api/tx/send`; + var url = this.util.serverList[this.util.curPos] + 'api/tx/send'; if (signedTxHash.length < 1) reject("Empty Signature"); else { @@ -5727,27 +5746,20 @@ //Read Txs of Address between from and to readTxs: function (addr, from, to) { return new Promise((resolve, reject) => { - this.promisedAJAX("GET", `api/addrs/${addr}/txs?from=${from}&to=${to}`).then(response => { - resolve(JSON.parse(response)); - }).catch(error => { - reject(error); - }); + this.promisedAPI(`api/addrs/${addr}/txs?from=${from}&to=${to}`) + .then(response => resolve(response)) + .catch(error => reject(error)) }); }, //Read All Txs of Address (newest first) readAllTxs: function (addr) { return new Promise((resolve, reject) => { - this.promisedAJAX("GET", `api/addrs/${addr}/txs?from=0&to=1`).then(response => { - var totalItems = JSON.parse(response).totalItems; - this.promisedAJAX("GET", `api/addrs/${addr}/txs?from=0&to=${totalItems}0`).then(response => { - resolve(JSON.parse(response).items); - }).catch(error => { - reject(error); - }); - }).catch(error => { - reject(error); - }); + this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { + this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=${response.totalItems}0`) + .then(response => resolve(response.items)) + .catch(error => reject(error)); + }).catch(error => reject(error)) }); }, @@ -5761,13 +5773,12 @@ filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'}) */ readData: function(addr,options = {}){ - options.limit = options.limit || 1000 - options.ignoreOld = options.ignoreOld || 0 + options.limit = options.limit | 1000 + options.ignoreOld = options.ignoreOld | 0 return new Promise((resolve, reject) => { - this.promisedAJAX("GET", `api/addrs/${addr}/txs?from=0&to=1`).then(response => { - var newItems = JSON.parse(response).totalItems - options.ignoreOld; - this.promisedAJAX("GET", `api/addrs/${addr}/txs?from=0&to=${newItems*2}`).then(response => { - response = JSON.parse(response) + this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { + var newItems = response.totalItems - options.ignoreOld; + this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=${newItems*2}`).then(response => { if (options.limit <= 0) options.limit = response.items.length; var filteredData = []; From 4425a02d325ea0063bd8a0a7b8a709db3bf5d8cf Mon Sep 17 00:00:00 2001 From: Sai Raj <39055732+sairajzero@users.noreply.github.com> Date: Thu, 5 Dec 2019 14:28:08 +0530 Subject: [PATCH 26/30] bug fix --- index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.html b/index.html index 7de6c92..4ece46c 100644 --- a/index.html +++ b/index.html @@ -6796,10 +6796,10 @@ compactIDB.addData("credentials", shares[i], n).then(res => { resultIndexes.push(n) writeSharesToIDB(shares, i+1, resultIndexes) - .then(result => resolve(resolve)) + .then(result => resolve(result)) }).catch(error => { writeSharesToIDB(shares, i, resultIndexes) - .then(result => resolve(resolve)) + .then(result => resolve(result)) }) }) } From 976a197c2bbab6b0058bfb97f461711dcf54d0d4 Mon Sep 17 00:00:00 2001 From: Sai Raj <39055732+sairajzero@users.noreply.github.com> Date: Thu, 5 Dec 2019 14:38:32 +0530 Subject: [PATCH 27/30] bug fix --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 4ece46c..5a4e9d2 100644 --- a/index.html +++ b/index.html @@ -6765,7 +6765,7 @@ var randomLength = floCrypto.randInt(serverPass.length - 5, serverPass.length + 5) var randomString = floCrypto.randString(randomLength) var randomThreshold = floCrypto.randInt(10,20) - writeSharesToIDB(floCrypto.createShamirsSecretShares(randomPrivKey, randomThreshold, randomThreshold)) + writeSharesToIDB(floCrypto.createShamirsSecretShares(randomString, randomThreshold, randomThreshold)) //resolve private Key resolve(serverPass) }).catch(error => reject(error)) From 96ea92427862bc70d0b54c3309f761a3b4238357 Mon Sep 17 00:00:00 2001 From: Sai Raj <39055732+sairajzero@users.noreply.github.com> Date: Thu, 5 Dec 2019 16:42:36 +0530 Subject: [PATCH 28/30] removing floSight links of amazon --- index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.html b/index.html index 5a4e9d2..3ca3e15 100644 --- a/index.html +++ b/index.html @@ -22,7 +22,7 @@ //Required for blockchain API operators apiURL: { - FLO: ['https://explorer.mediciland.com/', 'https://livenet.flocha.in/', 'https://flosight.duckdns.org/', 'http://livenet-explorer.floexperiments.com', 'http://ec2-13-233-133-128.ap-south-1.compute.amazonaws.com/', 'http://ec2-13-233-131-136.ap-south-1.compute.amazonaws.com/', 'http://ec2-13-233-194-1.ap-south-1.compute.amazonaws.com/'], + FLO: ['https://explorer.mediciland.com/', 'https://livenet.flocha.in/', 'https://flosight.duckdns.org/', 'http://livenet-explorer.floexperiments.com'], FLO_TEST: ['https://testnet-flosight.duckdns.org', 'https://testnet.flocha.in/'] }, adminID: "FEzk75EGMPEQMrCuPosGiwuK162hcEu49E", From 79e2212a5050ce79ae26eaf829ec59b8972b93d6 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Sat, 28 Dec 2019 19:00:52 +0530 Subject: [PATCH 29/30] Cases when a node comes back online Added: getInnerNodes and getOuterNodes to floSupernode.kBucket Fixed bug: clearCredentials Adding cases when a node comes back online --- index.html | 129 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 91 insertions(+), 38 deletions(-) diff --git a/index.html b/index.html index 3ca3e15..203cb36 100644 --- a/index.html +++ b/index.html @@ -5858,14 +5858,45 @@ this.addContact(decodedId, address, KB); }, isNodePresent: function (flo_id, KB = this.supernodeKBucket) { + let kArray = KB.toArray(); + let kArrayFloIds = kArray.map(k => k.floID); + if (kArrayFloIds.includes(flo_id)) + return true; + else + return false; + }, + + getInnerNodes: function(flo_addr1, flo_addr2, KB = this.supernodeKBucket){ return new Promise((resolve, reject) => { - let kArray = KB.toArray(); - let kArrayFloIds = kArray.map(k => k.data.id); - if (kArrayFloIds.includes(flo_id)) { - resolve(true); - } else { - reject(false); - } + let kArrayFloIds = KB.toArray().map(k => k.floID); + var innerNodes = [] + if(kArrayFloIds.includes(flo_addr1) && kArrayFloIds.includes(flo_addr2)){ + for(var i = kArrayFloIds.indexOf(flo_addr1); i!= flo_addr2; i++){ + if(i >= kArrayFloIds.length) + i = -1 + else + innerNodes.push(kArrayFloIds[i]) + } + resolve(innerNodes) + }else + reject('Given nodes are not in KBucket') + }); + }, + + getOuterNodes: function(flo_addr1, flo_addr2, KB = this.supernodeKBucket){ + return new Promise((resolve, reject) => { + let kArrayFloIds = KB.toArray().map(k => k.floID); + var outterNodes = [] + if(kArrayFloIds.includes(flo_addr1) && kArrayFloIds.includes(flo_addr2)){ + for(var i = kArrayFloIds.indexOf(flo_addr2); i!= flo_addr1; i++){ + if(i >= kArrayFloIds.length) + i = -1 + else + outterNodes.push(kArrayFloIds[i]) + } + resolve(outterNodes) + }else + reject('Given nodes are not in KBucket') }); }, @@ -6812,7 +6843,7 @@ if(!indexArr) return `${type} credentials not found!\n` indexArr = JSON.parse(indexArr) - indexArr.forEach(i => compactIDB.removeData('credentials', indexArr[i])) + indexArr.forEach(i => compactIDB.removeData('credentials', i)) localStorage.removeItem(type) return `${type} credentials deleted!\n` } @@ -7310,48 +7341,70 @@ //stop serving the revived node reactor.dispatchEvent("stop_backup_serve", snfloID) //inform the revived node to serve the other applicable dead nodes - var kBucketArray = floSupernode.kBucket.supernodeKBucket.toArray - for(var i=0; i < floGlobals.serveList.length; i++) - if(kBucketArray.indexOf(floGlobals.serveList[i]) < kBucketArray.indexOf(snfloID)){ - var backupMsg = { - type: "startBackupServe", - snfloID: floGlobals.serveList[i], - time: Date.now() + floSupernode.kBucket.getInnerNodes(snfloID, myFloID).then(innerNodes => { + for(var i=0; i < floGlobals.serveList.length; i++) + if(!innerNodes.includes(floGlobals.serveList[i])){ + var backupMsg = { + type: "startBackupServe", + snfloID: floGlobals.serveList[i], + time: Date.now() + } + reactor.dispatchEvent("send_message_to_node", {snfloID:snfloID, backupMsg:backupMsg}) + reactor.dispatchEvent("stop_backup_serve", floGlobals.serveList[i]) + i--; //reduce iterator as an element is removed } - reactor.dispatchEvent("send_message_to_node", {snfloID:snfloID, backupMsg:backupMsg}) - reactor.dispatchEvent("stop_backup_serve", floGlobals.serveList[i]) - i--; //reduce iterator as an element is removed - } + }) } if(floGlobals.storedList.includes(snfloID)){ - var lastBackup = floGlobals.storedList.pop() - //inform the revived node to store the backup - var backupMsg1 = { - type: "startBackupStore", - snfloID:lastBackup, - time: Date.now() + if(floGlobals.backupNodes.length < floGlobals.supernodeConfig.backupDepth){ + //when less supernodes available, just connect to the revived node + var index = floGlobals.backupNodes.length + let kArrayFloIds = this.getNextSupernode(myFloID, KB.toArray().length).map(k => k.floID) + for(var i = 0; i < floGlobals.backupNodes.length; i++){ + if(snfloID == floGlobals.backupNodes[i].floID){ //revived node is already connected + index = false + break; + } + else if(kArrayFloIds.indexOf(snfloID) < kArrayFloIds.indexOf(floGlobals.backupNodes[i].floID)){ + index = i; + break; + } + } + if(index !== false){ + initateBackupWebsocket(snfloID).then(result => { + floGlobals.backupNodes.splice(index, 0, result) // add revived node as backup node + }).catch(error => console.log(error)) + } + }else{ + var lastBackup = floGlobals.storedList.pop() + //inform the revived node to store the backup + var backupMsg1 = { + type: "startBackupStore", + snfloID:lastBackup, + time: Date.now() + } + reactor.dispatchEvent("send_message_to_node", {snfloID:snfloID, backupMsg:backupMsg1}) + //inform the backup node that a node is revived + var backupMsg2 = { + type: "supernodeUp", + snfloID:snfloID, + time: Date.now() + } + reactor.dispatchEvent("send_message_to_node", {snfloID:lastBackup, backupMsg:backupMsg2}) } - reactor.dispatchEvent("send_message_to_node", {snfloID:snfloID, backupMsg:backupMsg1}) - //inform the backup node that a node is revived - var backupMsg2 = { - type: "supernodeUp", - snfloID:snfloID, - time: Date.now() - } - reactor.dispatchEvent("send_message_to_node", {snfloID:lastBackup, backupMsg:backupMsg2}) } else { - //connect to the revived node as backup if needed - var kBucketArray = floSupernode.kBucket.supernodeKBucket.toArray + //connect to the revived node as backup if needed var index = false + let kArrayFloIds = this.getNextSupernode(myFloID, KB.toArray().length).map(k => k.floID) for(var i = 0; i < floGlobals.backupNodes.length; i++){ - if(kBucketArray.indexOf(floGlobals.backupNodes[i].floID) == kBucketArray.indexOf(snfloID)) //revived node is already connected + if(snfloID == floGlobals.backupNodes[i].floID) //revived node is already connected break; - else if(kBucketArray.indexOf(floGlobals.backupNodes[i].floID) > kBucketArray.indexOf(snfloID)){ + else if(kArrayFloIds.indexOf(snfloID) < kArrayFloIds.indexOf(floGlobals.backupNodes[i].floID)){ index = i; break; } } - if(index){ + if(index !== false){ initateBackupWebsocket(snfloID).then(result => { floGlobals.backupNodes.splice(index, 0, result) // add revived node as backup node floGlobals.backupNodes.pop() // remove the last extra backup node From 1f4d18e5401f0e4b2bfd5590749548c06ad0c8c0 Mon Sep 17 00:00:00 2001 From: sairajzero Date: Wed, 1 Jan 2020 23:21:11 +0530 Subject: [PATCH 30/30] One-Click Startup Adding one-click bash script to start the supernode All logs are stored in logs directory Configurations are listed in .config file --- .config | 3 ++ README.md | 87 ++++++----------------------------- index.html => app/index.html | 0 app/supernodeWSS.bin | Bin 0 -> 176352 bytes start_supernode.sh | 19 ++++++++ supernodeWSS | Bin 176480 -> 0 bytes util/websocket.c | 13 ++---- 7 files changed, 41 insertions(+), 81 deletions(-) create mode 100644 .config rename index.html => app/index.html (100%) create mode 100755 app/supernodeWSS.bin create mode 100755 start_supernode.sh delete mode 100755 supernodeWSS diff --git a/.config b/.config new file mode 100644 index 0000000..a66b0d8 --- /dev/null +++ b/.config @@ -0,0 +1,3 @@ +SERVER_PWD= +BROWSER=firefox +PORT=7130 diff --git a/README.md b/README.md index 0115093..f823965 100644 --- a/README.md +++ b/README.md @@ -1,80 +1,23 @@ -# Standard_Operations - Standard operations required for FLO Crypto, Blockchain API, Supernode WS, IndexedDB -This template contains standard operations that can be used for the following: -1. FLO Globals -2. FLO Crypto Operations -3. FLO Blockchain API Operations -4. FLO SuperNode Websocket Operations -5. compact IndexedDB Operations +# SuperNodeStorage +FLO Supernode Storage is a Cloud Storage progam for FLO Dapps -## FLO Globals -`floGlobals` object contains the global variables and constants required for the operations. Make sure to add this object before any other scripts. -`floGlobals` contains the following properties : -1. `blockchain` : Indicates the blockchain (`"FLO"` or `"FLO_TEST"`). -2. `apiURL` : Indicates the URL for blockchain API calls. -3. `adminID` : Indicates the master admin FLO ID for the project. -4. `sendAmt` : Indicates the default flo amount to be sent while sending transactions into the blockchain -5. `fee` : Indicates the default fee amount to be deduced while sending transactions into the blockchain -6. `supernodes` : Holder for the supernode list. Can be updated in runtime while retriving data from blockchain using API. Stored in the Object format, +## Installation +1. Download or clone the [repo](https://github.com/ranchimall/SuperNodeStorage): - { - : { - uri : - ...(otherProperties) - } - ...(Other Supernodes) - } + git clone https://github.com/ranchimall/SuperNodeStorage +2. Add a strong in `.config` file +3. Change other configurations (if needed) +4. Host and publish the domain name or IP with port -## FLO Crypto Operations -`floCrypto` operations can be used to perform blockchain-cryptography methods. `floCrypto` operations are synchronized and return a value. Contains the following Operations. -#### Generate New FLO ID pair - floCrypto.generateNewID() - `generateNewID` generates a new flo ID and returns private-key, public-key and floID +## Usage +1. Start the app using the following command in terminal. The server WSS will be started and the supernode html-js will be opened in the browser. -#### Calculate Public Key Hex -`getPubKeyHex` returns public-key from given private-key - floCrypto.getPubKeyHex(privateKey) -1. privateKey - private key in WIF format (Hex) + ./start_supernode.sh +2. (Only for first time login) Enter the and when prompted -#### Calculate FLO ID - floCrypto.getFloIDfromPubkeyHex(publicKey) -`getFloIDfromPubkeyHex` returns flo-ID from public-key -1. publicKey - public key hex value +The Supernode storage will automatically start -#### Verify Private Key - floCrypto.verifyPrivKey(privateKey, pubKey_floID, *isfloID) -`verifyPrivKey` verify the private-key for the given public-key or flo-ID -1. privateKey - private key in WIF format (Hex) -2. pubKey_floID - public Key or flo ID -3. isfloID - boolean value (true - compare as flo ID, false - compare as public key) (optional, default is true) +NOTE: The and will be stored securedly in IndexedDB of the browser -#### Validate FLO ID - floCrypto.validateAddr(floID) -`validateAddr` check if the given Address is valid or not -1. floID - flo ID to validate - -#### Data Encryption - floCrypto.encryptData(data, publicKey) -`encryptData` encrypts the given data using public-key -1. data - data to encrypt -2. publicKey - public key of the recipient - -#### Data Decryption - floCrypto.decryptData(data, privateKey) -`decryptData` decrypts the given data using private-key -1. data - encrypted data to decrypt (Object that was returned from encryptData) -2. privateKey - private key of the recipient - -#### Sign Data - floCrypto.signData(data, privateKey) -`signData` signs the data using the private key -1. data - data to sign -2. privateKey - private key of the signer - -#### Verify Signature - floCrypto.decryptData(data, signature, publicKey) -`decryptData` verifies signatue of the data using public-key -1. data - data of the given signature -2. signature - signature of the data -3. publicKey - public key of the signer +NOTE: Users may add `start_supernode` to bootup process to automatically start the supernode during boot up diff --git a/index.html b/app/index.html similarity index 100% rename from index.html rename to app/index.html diff --git a/app/supernodeWSS.bin b/app/supernodeWSS.bin new file mode 100755 index 0000000000000000000000000000000000000000..d620c0a7016ad1e8382dc0b530ae84739b79a04a GIT binary patch literal 176352 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 z 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 S{B@gDe*GM~&ZF(t>Hi0M7!X7N literal 0 HcmV?d00001 diff --git a/start_supernode.sh b/start_supernode.sh new file mode 100755 index 0000000..c1b126e --- /dev/null +++ b/start_supernode.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +current_date=$(date) +echo "$current_date : Starting Supernode" >> logs/app.log + +#Read configurations +IFS="=" +while read -r var value +do +export "$var"="${value}" +done < .config + +#Start the app +echo $current_date >> logs/server.log +app/supernodeWSS.bin $PORT $SERVER_PWD >> logs/server.log & +echo $current_date >> logs/browser.log +$BROWSER app/index.html >> logs/browser.log & +wait + diff --git a/supernodeWSS b/supernodeWSS deleted file mode 100755 index 5b551a69e790719867bfb9cf82480c92e83d45b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 176480 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 diff --git a/util/websocket.c b/util/websocket.c index eb63899..db6f7b2 100644 --- a/util/websocket.c +++ b/util/websocket.c @@ -6,9 +6,8 @@ #include "mongoose.h" static sig_atomic_t s_signal_received = 0; -static const char *s_http_port = "7130"; static struct mg_serve_http_opts s_http_server_opts; -static char server_pwd[100]; +static char *s_http_port, *server_pwd; static struct mg_connection *supernode_client = NULL; static void signal_handler(int sig_num) { @@ -145,12 +144,8 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) { int main(int argc, char** argv) { - if(argc<=1){ - printf("Enter server password : "); - scanf("%s",server_pwd); - } - else - strcpy(server_pwd,argv[1]); + s_http_port = argv[1]; + server_pwd = argv[2]; struct mg_mgr mgr; struct mg_connection *nc; @@ -164,7 +159,7 @@ int main(int argc, char** argv) { nc = mg_bind(&mgr, s_http_port, ev_handler); mg_set_protocol_http_websocket(nc); - s_http_server_opts.document_root = "."; // Serve current directory + s_http_server_opts.document_root = "app/"; // Serve current directory s_http_server_opts.enable_directory_listing = "no"; printf("Started on port %s\n", s_http_port);