192 lines
6.3 KiB
JavaScript
192 lines
6.3 KiB
JavaScript
'use strict';
|
|
const shareThreshold = 70 / 100;
|
|
|
|
var DB; //Container for database
|
|
|
|
//Backup Transfer
|
|
function sendBackup(timestamp, ws) {
|
|
if (!timestamp) timestamp = 0;
|
|
else if (typeof timestamp === "string" && /\.\d{3}Z$/.test(timestamp))
|
|
timestamp = timestamp.substring(0, timestamp.length - 1);
|
|
let promises = [
|
|
send_dataSync(timestamp, ws),
|
|
send_deleteSync(timestamp, ws),
|
|
send_dataImmutable(timestamp, ws)
|
|
];
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedSync = [];
|
|
result.forEach(r => r.status === "rejected" ? failedSync.push(r.reason) : null);
|
|
if (failedSync.length) {
|
|
console.info("Backup Sync Failed:", failedSync);
|
|
ws.send(JSON.stringify({
|
|
mode: "SYNC_END",
|
|
status: false,
|
|
info: failedSync
|
|
}));
|
|
} else {
|
|
console.info("Backup Sync completed");
|
|
ws.send(JSON.stringify({
|
|
mode: "SYNC_END",
|
|
status: true
|
|
}));
|
|
}
|
|
});
|
|
}
|
|
|
|
function send_deleteSync(timestamp, ws) {
|
|
return new Promise((resolve, reject) => {
|
|
DB.query("SELECT * FROM _backup WHERE mode is NULL AND timestamp > ?", [timestamp]).then(result => {
|
|
ws.send(JSON.stringify({
|
|
mode: "SYNC_DELETE",
|
|
delete_data: result
|
|
}));
|
|
resolve("deleteSync");
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("deleteSync");
|
|
});
|
|
})
|
|
}
|
|
|
|
function send_dataSync(timestamp, ws) {
|
|
const sendTable = (table, id_list) => new Promise((res, rej) => {
|
|
DB.query(`SELECT * FROM ${table} WHERE id IN (${id_list})`)
|
|
.then(data => {
|
|
ws.send(JSON.stringify({
|
|
table,
|
|
mode: "SYNC_ADD_UPDATE",
|
|
data
|
|
}));
|
|
res(table);
|
|
}).catch(error => {
|
|
console.error(error);
|
|
rej(table);
|
|
});
|
|
});
|
|
return new Promise((resolve, reject) => {
|
|
DB.query("SELECT * FROM _backup WHERE mode=TRUE AND timestamp > ?", [timestamp]).then(result => {
|
|
let sync_needed = {};
|
|
result.forEach(r => r.t_name in sync_needed ? sync_needed[r.t_name].push(r.id) : sync_needed[r.t_name] = [r.id]);
|
|
ws.send(JSON.stringify({
|
|
mode: "SYNC_ADD_UPDATE_HEADER",
|
|
add_data: result
|
|
}));
|
|
let promises = [];
|
|
for (let table in sync_needed)
|
|
promises.push(sendTable(table, sync_needed[table]));
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedTables = [];
|
|
result.forEach(r => r.status === "rejected" ? failedTables.push(r.reason) : null);
|
|
if (failedTables.length)
|
|
reject(["dataSync", failedTables]);
|
|
else
|
|
resolve("dataSync");
|
|
});
|
|
}).catch(error => {
|
|
console.error(error);
|
|
reject("dataSync");
|
|
});
|
|
});
|
|
}
|
|
|
|
function send_dataImmutable(timestamp, ws) {
|
|
const immutable_tables = {
|
|
Users: "created",
|
|
Request_Log: "request_time",
|
|
Transactions: "tx_time",
|
|
priceHistory: "rec_time"
|
|
};
|
|
const sendTable = (table, timeCol) => new Promise((res, rej) => {
|
|
DB.query(`SELECT * FROM ${table} WHERE ${timeCol} > ?`, [timestamp])
|
|
.then(data => {
|
|
ws.send(JSON.stringify({
|
|
table,
|
|
mode: "SYNC_ADD_IMMUTABLE",
|
|
data
|
|
}));
|
|
res(table);
|
|
}).catch(error => {
|
|
console.error(error);
|
|
rej(table);
|
|
});
|
|
});
|
|
|
|
return new Promise((resolve, reject) => {
|
|
let promises = [];
|
|
for (let table in immutable_tables)
|
|
promises.push(sendTable(table, immutable_tables[table]));
|
|
Promise.allSettled(promises).then(result => {
|
|
let failedTables = [];
|
|
result.forEach(r => r.status === "rejected" ? failedTables.push(r.reason) : null);
|
|
if (failedTables.length)
|
|
reject(["dataImmutable", failedTables]);
|
|
else
|
|
resolve("dataImmutable");
|
|
});
|
|
})
|
|
}
|
|
|
|
//Shares
|
|
function generateNewSink() {
|
|
let sink = floCrypto.generateNewID();
|
|
let nextNodes = KB.nextNode(myFloID, null);
|
|
let shares = floCrypto.createShamirsSecretShares(sink.privKey, nextNodes.length, Math.ceil(nextNodes.length * shareThreshold));
|
|
sink.shares = {};
|
|
for (let i in nextNodes)
|
|
sink.shares[nextNodes[i]] = shares[i];
|
|
return sink;
|
|
}
|
|
|
|
function sendShare(ws, sinkID, share) {
|
|
ws.send(JSON.stringify({
|
|
command: "SINK_SHARE",
|
|
sinkID,
|
|
keyShare
|
|
}));
|
|
}
|
|
|
|
//Transmistter
|
|
var nodeList; //Container for (backup) node list
|
|
function startBackupTransmitter(wss) {
|
|
wss.on('connection', ws => {
|
|
ws.on('message', message => {
|
|
//verify if from a backup node
|
|
try {
|
|
let invalid = null,
|
|
request = JSON.parse(message);
|
|
console.debug(request);
|
|
if (!backupIDs.includes(request.floID))
|
|
invalid = "FLO ID not approved for backup";
|
|
else if (request.floID !== floCrypto.getFloID(request.pubKey))
|
|
invalid = "Invalid pubKey";
|
|
else if (!floCrypto.verifySign(request.type + "|" + request.req_time, request.sign, request.pubKey))
|
|
invalid = "Invalid signature";
|
|
else if (request.type !== "BACKUP_SYNC")
|
|
invalid = "Invalid Request Type";
|
|
//TODO: check if request time is valid;
|
|
if (invalid)
|
|
ws.send(JSON.stringify({
|
|
error: invalid
|
|
}));
|
|
else
|
|
sendBackup(request.last_time, ws);
|
|
} catch (error) {
|
|
console.error(error);
|
|
ws.send(JSON.stringify({
|
|
mode: "SYNC_ERROR",
|
|
error: 'Unable to process the request!'
|
|
}));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
module.exports = {
|
|
init: startBackupTransmitter,
|
|
set nodeList(ids) {
|
|
nodeList = ids;
|
|
},
|
|
set DB(db) {
|
|
DB = db;
|
|
}
|
|
}; |