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);
+ })
+
}
+