diff --git a/app/index.html b/app/index.html index 30db3a9..20dc343 100644 --- a/app/index.html +++ b/app/index.html @@ -31,14 +31,19 @@ //Required for Supernode operations supernodes: {}, //each supnernode must be stored as floID : {uri:,pubKey:} - diskList : ["General"], defaultDisk : "General", - applicationList:{}, + appList:{}, appSubAdmins:{}, serveList : [], storedList : [], - supernodeConfig : {}, - backupNodes : [] + backupNodes : [], + supernodeConfig : {} + /* List of supernode configurations (all blockchain controlled by SNStorageID) + backupDepth - (Interger) Number of backup nodes + refreshDelay - (Interger) Count of requests for triggering read-blockchain and autodelete + deleteDelay - (Interger) Maximum number of duration (milliseconds) an unauthorised data is stored + errorFeedback - (Boolean) Send error (if any) feedback to the requestor + */ } @@ -7532,7 +7537,7 @@ Bitcoin.Util = { }, //generate a random String within length (options : alphaNumeric chars only) - randString: function (length, alphaNumeric = false) { + randString: function (length, alphaNumeric = true) { var result = ''; if (alphaNumeric) var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; @@ -7630,20 +7635,18 @@ Bitcoin.Util = { if (key.priv == null) return null; key.setCompressed(true); - var pubkeyHex = key.getPubKeyHex(); - return pubkeyHex; + return key.getPubKeyHex(); }, //Returns flo-ID from public-key or private-key getFloID: function (keyHex) { - if(!pubkeyHex) + if(!keyHex) return null; try { - var key = new Bitcoin.ECKey(privateKeyHex); + var key = new Bitcoin.ECKey(keyHex); if (key.priv == null) - key.setPub(pubkeyHex); - var floID = key.getBitcoinAddress(); - return floID; + key.setPub(keyHex); + return key.getBitcoinAddress(); } catch (e) { return null; } @@ -8196,7 +8199,7 @@ Bitcoin.Util = { return KB.distance(KB.localNodeId, decodedId); }, - closest: function (floID, n, KB) { + closestOf: function (floID, n, KB) { let decodedId = this.decodeID(floID); return KB.closest(flo_addr, n) }, @@ -8218,7 +8221,7 @@ Bitcoin.Util = { let superNodeList = Object.keys(floGlobals.supernodes); let masterID = floGlobals.SNStorageID; this.SNKB = this.util.constructKB(superNodeList, masterID); - this.SNCO = superNodeList.map(sn => [this.util.distance(sn, this.SNKB), sn]) + this.SNCO = superNodeList.map(sn => [this.util.distanceOf(sn, this.SNKB), sn]) .sort((a, b) => a[0] - b[0]) .map(a => a[1]) console.log(this.SNCO) @@ -8285,7 +8288,7 @@ Bitcoin.Util = { }, closestNode: function (id, n = 1) { - let cNodes = this.util.closest(id, n).map(k => k.floID) + let cNodes = this.util.closestOf(id, n).map(k => k.floID) return (n == 1 ? cNodes[0] : cNodes) } }, @@ -8297,10 +8300,7 @@ Bitcoin.Util = { var wsConn = new WebSocket("wss://" + floGlobals.supernodes[snID].uri + "/ws"); wsConn.onmessage = (evt) => { if (evt.data == '$+') - resolve({ - snID, - wsConn - }) + resolve(wsConn) else if (evt.data == '$-') { wsConn.close(); reject(`${snID} is not active`) @@ -8328,33 +8328,6 @@ Bitcoin.Util = { }) }, - //Sends data to the supernode - sendData: function (data, floID) { - return new Promise((resolve, reject) => { - this.connectActive(floID).then(node => { - node.wsConn.send(data); - node.wsConn.close(); - resolve(`Data sent to supernode : ${node.snID}`) - }).catch(error => reject(error)); - }); - }, - - //Request data from supernode - requestData: function (request, floID) { - return new Promise((resolve, reject) => { - this.connectActive(floID).then(node => { - node.wsConn.onmessage = (evt) => { - if (evt.data[0] != '$') - resolve(evt.data); - else - reject(evt.data) - node.wsConn.close(); - } - node.wsConn.send(`?${request}`) - }).catch(error => reject(error)); - }); - }, - //Supernode initate (call this function only when client is authorized as supernode) initSupernode: function (pwd, floID) { return new Promise((resolve, reject) => { @@ -8375,13 +8348,13 @@ Bitcoin.Util = { if (evt.data[0] == '$') { console.log('Admin Message :', evt.data); if (evt.data == '$Access Granted!') - resolve("Access Granted! Initiated Supernode client"); + resolve("Access Granted: Initiated Supernode"); else if (evt.data == '$Access Denied!') - reject("Access Denied! Failed to initiate Supernode client"); - } else if (evt.data[0] == '?') - processIncomingRequest(evt.data.substr(1)); - else - processIncomingData(evt.data) + reject("Access Denied: Failed to initiate Supernode"); + else if (evt.data == '$Access Locked') + reject("Access Locked: Another instance of Supernode is active") + } else + processIncomingData(evt.data); }; this.supernodeClientWS.onerror = (evt) => { console.error('Error! Unable to connect supernode websocket!'); @@ -8395,84 +8368,126 @@ Bitcoin.Util = { } //Process incoming request from clients - function processIncomingRequest(request) { - console.log('Request :', request); + function processIncomingData(data) { + console.log(data); try { - request = request.split(" "); - requestor = request.shift(); - request = JSON.parse(request.join(" ")); - let closeNode = floSupernode.kBucket.closestNode(request.receiverID) - if (floGlobals.serveList.includes(closeNode)) { - var filterOptions = { - lowerKey: request.lowerVectorClock, - upperKey: request.upperVectorClock, - lastOnly: request.mostRecent, - atKey: request.atVectorClock, - patternEval: (k, v) => { - return (v.application == request.application && 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.diskList.includes(request.application) ? request.application : - floGlobals.defaultDisk, filterOptions, `SN_${closeNode}`) - .then(result => floSupernode.supernodeClientWS.send(`${requestor} ${JSON.stringify(result)}`)) - .catch(error => console.error(error)) + let gid = evt.data.substring(0, 34); + let uid = evt.data.substring(35, 40); + let data = JSON.parse(evt.data.substring(41)); + if (data.from in floGlobals.supernodes && data.sn_msg) + processTaskFromSupernode(gid, uid, data); + else { + let curTime = Date.now() + if(data.time > curTime + floGlobals.supernodeConfig.delayDelta || + data.time < curTime + floGlobals.supernodeConfig.delayDelta) + throw Error("Time deviation longer than allowed delay"); + else if (data.request) + processRequestFromUser(gid, uid, data); + else if (data.message) + processDataFromUser(gid, uid, data); + else if (data.delete) + processDeleteFromUser(gid, uid, data); } } catch (error) { - console.log(error.message) + console.error(error) + if(floGlobals.supernodeConfig.errorFeedback) + floSupernode.supernodeClientWS.send(`@${uid}#${gid}:${error}`) } } - //Process Incoming data - function processIncomingData(data) { - console.log('Data :', data); - try { - data = JSON.parse(data) - if (data.from in floGlobals.supernodes && data.sn_msg) - processDataFromSupernode(data); - else { //Serving Users - - //Delete request from receiver - if (data.delete) { - let closeNode = floSupernode.kBucket.closestNode(data.from); - if (floGlobals.serveList.includes(closeNode) && - data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && - floCrypto.verifySign(JSON.stringify(data.delete), data.sign, data.pubKey)) { - //start the deletion process - - //indicate backup nodes to delete data - } - } else { - let closeNode = floSupernode.kBucket.closestNode(data.receiverID) - if (floGlobals.serveList.includes(closeNode) && - data.senderID == floCrypto.getFloIDfromPubkeyHex(data.pubKey) && - floCrypto.verifySign(JSON.stringify(data.message), data.sign, data.pubKey)) { - var key = `${Date.now()}_${data.senderID}` - var value = { - senderID: data.senderID, - receiverID: data.receiverID, - pubKey: data.pubKey, - message: data.message, - sign: data.sign, - application: data.application, - type: data.type, - comment: data.comment - } - compactIDB.addData(floGlobals.diskList.includes(value.application) ? value.application : - floGlobals - .defaultDisk, value, key, `SN_${closeNode}`) - sendBackupData(key, value, closeNode); - } - } - } - - if ((refreshData.countdown--) <= 0) - refreshData(); - } catch (error) { - console.log(error.message); + //Process request from users + function processRequestFromUser(gid, uid, data) { + let request = data.request; + if (!floCrypto.validateAddr(request.receiverID)) + throw Error("Invalid receiverID") + let closeNode = floSupernode.kBucket.closestNode(request.receiverID) + if (!floGlobals.serveList.includes(closeNode)) + throw Error("Incorrect Supernode") + var filterOptions = { + lowerKey: request.lowerVectorClock, + upperKey: request.upperVectorClock, + lastOnly: request.mostRecent, + atKey: request.atVectorClock } + filterOptions.patternEval = ( + v.application == request.application && + 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(request.application in floGlobals.appList ? request.application : + floGlobals.defaultDisk, filterOptions, `SN_${closeNode}`) + .then(result => floSupernode.supernodeClientWS.send(`@${uid}#${gid}:${JSON.stringify(result)}`)) + .catch(error => { + throw Error("Invalid request") + }) + } + + //Process data from users + function processDataFromUser(gid, uid, data) { + if (!floCrypto.validateAddr(data.receiverID)) + throw Error("Invalid receiverID") + let closeNode = floSupernode.kBucket.closestNode(data.receiverID) + if (!floGlobals.serveList.includes(closeNode)) + throw Error("Incorrect Supernode") + if (data.senderID !== floCrypto.getFloID(data.pubKey)) + throw Error("Invalid senderID/pubKey") + let hashcontent = ["receiverID", "time", "application", "type", "message", "comment"] + .map(d => data[d]).join("|") + if (!floCrypto.verifySign(hashcontent, data.sign, data.pubKey)) + throw Error("Invalid signature") + var key = `${Date.now()}_${data.senderID}` + var value = { + senderID: data.senderID, + receiverID: data.receiverID, + pubKey: data.pubKey, + time: data.time, + message: data.message, + sign: data.sign, + application: data.application, + type: data.type, + comment: data.comment + } + compactIDB.addData(value.application in floGlobals.appList ? value.application : + floGlobals.defaultDisk, value, key, `SN_${closeNode}`).then(result => { + floSupernode.supernodeClientWS.send(`@${uid}#${gid}:${JSON.stringify({vectorClock: key})}`) + floSupernode.supernodeClientWS.send(`#${data.receiverID}:${JSON.stringify({[key]: value})}`) + sendBackupData(key, value, closeNode); + }).catch(error => { + throw Error("Invalid Data") + }) + } + + function processDeleteFromUser(gid, uid, data) { + if (!floCrypto.validateAddr(data.requestorID)) + throw Error("Invalid requestorID") + let closeNode = floSupernode.kBucket.closestNode(data.requestorID) + if (!floGlobals.serveList.includes(closeNode)) + throw Error("Incorrect Supernode") + if (data.requestorID !== floCrypto.getFloID(data.pubKey)) + throw Error("Invalid senderID/pubKey") + let hashcontent = ["time", "application", "delete"] + .map(d => data[d]).join("|") + throw Error("Invalid signature") + let disk = data.application in floGlobals.appList ? data.application : floGlobals.defaultDisk; + const deleteData = v => new Promise((res, rej) => { + compactIDB.readData(disk, vc, `SN_${closeNode}`).then(result => { + if(result.receiverID === data.requestorID) + compactIDB.removeData(disk, vc, `SN_${closeNode}`) + .then(r => res(true)).catch(e => res(false)) + else + res(false) + }).catch(e => res(false)) + }) + Promise.all(data.delete.map(vc => deleteData(vc))).then(result => { + floSupernode.supernodeClientWS.send(`@${uid}#${gid}:${JSON.stringify(result)}`) + let vectorClocks = [] + for(let i in result) + if(result[i]) vectorClocks.push(data.delete[i]) + sendBackupDelete(data.application, vectorClocks, closeNode); + }) + } +