From ab6fab887ca7e6b63750aaf8ed5e21a20538a306 Mon Sep 17 00:00:00 2001 From: Abhishek Sinha Date: Mon, 13 May 2019 19:27:32 +0530 Subject: [PATCH] added code for primary supernode to get data from backup supernodes --- supernode/index.html | 347 +++++++++++++++++++++++++++++-------------- 1 file changed, 238 insertions(+), 109 deletions(-) diff --git a/supernode/index.html b/supernode/index.html index 629b238..6bbfd09 100644 --- a/supernode/index.html +++ b/supernode/index.html @@ -10129,16 +10129,47 @@ "receiver_flo_address": localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, }).then(sync_request=>doSend(sync_request)); }, + + sync_primary_supernode_from_backup_supernode: function (primary_su="", backup_su="") { + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC.send_rpc.call(this, + "sync_primary_supernode_from_backup_supernode", { + "trader_flo_address": primary_su, + "job": "SYNC_PRIMARY_SUPERNODE_DB_WITH_BACKUP_SUPERNODE_DB", + "receiver_flo_address": backup_su, + }).then(sync_request=>doSend(sync_request, primary_su)); + }, - get_sharable_db_data: async function (dbTableNamesArray) { + get_sharable_db_data: async function (dbTableNamesArray, backup_db="") { let arr = {}; + if (typeof backup_db=="string" && backup_db.length>0) { + if (typeof localbitcoinplusplus.newBackupDatabase.db[backup_db] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[backup_db]; + readAllDB = foreign_db.backup_readAllDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } for (const elem of dbTableNamesArray) { await readAllDB(elem).then(e => arr[elem] = e); } return arr; }, - get_sharable_db_data_for_single_user: async function (dbTableNamesArray) { + + get_sharable_db_data_for_single_user: async function (dbTableNamesArray, backup_db="") { let arr = {}; + if (typeof backup_db=="string" && backup_db.length>0) { + if (typeof localbitcoinplusplus.newBackupDatabase.db[backup_db] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[backup_db]; + readAllDB = foreign_db.backup_readAllDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } for (const elem of dbTableNamesArray) { await readDBbyIndex(elem).then(e => arr[elem] = e); } @@ -10235,6 +10266,45 @@ return new Promise(function(resolve) { setTimeout(resolve.bind(null, v), t) }); + }, + + checkIfAllPreviousSupernodesAreDeadForAUserNode: (userFLoID="") => { + return new Promise(async (resolve, reject)=>{ + + if (typeof userFLoID !== "string" || userFLoID.length<1) { + console.warn(`Invalid FLO Id`); + return; + } + + const s_id = await localbitcoinplusplus.kademlia.determineClosestSupernode(userFLoID); + const primarySuFloId = s_id[0].data.id; + + let getSupernodeClosestSuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode("", 3, supernodeKBucket, primarySuFloId); + + let promises = []; + let leaving_supernode_flo_id = ""; + + for (let index = 0; index < getSupernodeClosestSuObj.length; index++) { + const element = getSupernodeClosestSuObj[index]; + if (element.data.id==localbitcoinplusplus.wallets.my_local_flo_address) break; + promises.push(readDBbyIndex('myClosestSupernodes', 'trader_flo_address', element.data.id)); + leaving_supernode_flo_id = element.data.id; + } + + Promise.all(promises).then(cs=>{ + let isPreviousSupernodesLive = cs.map((su_status, index)=> + (typeof su_status[index] !== "object" || su_status[index].is_live == true ) + ); + + if (!isPreviousSupernodesLive.includes(true)) { + resolve(true); // Every previous supernode is dead + } else { + resolve(false); // At least one previous supernode is alive + } + }).catch(e=>reject(e)); + }); + } } @@ -11175,16 +11245,17 @@ if (typeof user_keys == "object" && typeof user_keys.pubKeyHex == "string") { if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(user_keys.pubKeyHex)) { if (typeof flo_id !== null || typeof flo_id !== 'undefined') { - // let karr = KBucket.toArray(); - // let karr_floIds = karr.map(f=>f.data.id); - // if (!karr_floIds.includes(flo_id)) { - // return callback(false); - // } - - localbitcoinplusplus.kademlia.determineClosestSupernode(flo_id) + localbitcoinplusplus.kademlia.determineClosestSupernode(flo_id, 4) .then(my_closest_su=>{ if (user_keys.address === my_closest_su[0].data.id) { return callback(true); + } else { + let su_arr = my_closest_su.map(m=>m.data.id); + if(su_arr.includes(flo_id)) { + return callback(true); + } else { + return callback(false); + } } }); } else { @@ -12452,6 +12523,17 @@ request.response = {}; let err_msg; + if(typeof params.trader_flo_address !="string") return; + const my_closest_su_list = await localbitcoinplusplus.kademlia.determineClosestSupernode(params.trader_flo_address); + const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser]; + + if(typeof backup_server_db_instance !== "object") { + let backup_db_error_msg = `WARNING: Unknown DB instance. DB Backup failed.`; + showMessage(backup_db_error_msg); + throw new Error(backup_db_error_msg); + }; + if (method=="sync_with_supernode") { RM_RPC.filter_legit_backup_requests(params.trader_flo_address, function (is_valid_request) { if (is_valid_request === true && params.job == @@ -12477,22 +12559,36 @@ }); } + if (method=="sync_primary_supernode_from_backup_supernode") { + // params.trader_flo_address -> primary supernode flo id + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, function (is_valid_request) { + if (is_valid_request === true && params.job == + "SYNC_PRIMARY_SUPERNODE_DB_WITH_BACKUP_SUPERNODE_DB" && params.trader_flo_address.length > + 0) { + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", "cash_balances", "crypto_balances"]; + + localbitcoinplusplus.actions.get_sharable_db_data(tableArray, params.trader_flo_address).then( + function (su_db_data) { + if (typeof su_db_data == "object") { + su_db_data.trader_flo_address = params.trader_flo_address; + su_db_data.receiver_flo_address = params.trader_flo_address; + RM_RPC + .send_rpc + .call(this, "sync_primary_supernode_from_backup_supernode_response", + su_db_data) + .then(server_sync_response=> + doSend(server_sync_response)); + } + }); + } + }); + } + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, async function (is_valid_request) { if (is_valid_request !== true) return false; try { - - if(typeof params.trader_flo_address !="string") return; - const my_closest_su_list = await localbitcoinplusplus.kademlia.determineClosestSupernode(params.trader_flo_address); - const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser]; - - if(typeof backup_server_db_instance !== "object") { - let backup_db_error_msg = `WARNING: Unknown DB instance. DB Backup failed.`; - showMessage(backup_db_error_msg); - throw new Error(backup_db_error_msg); - }; - // CHECK HERE IF USER IS INDULGED IN ANY MORE TRADE. IF TRUE RETURN ERROR + // CHECK HERE IF USER IS INDULGED IN ANY MORE TRADE. IF TRUE RETURN ERROR await backup_server_db_instance.backup_readAllDB("deposit").then(function (res) { if (typeof res == "object" && res.length > 0) { let canUserTrade = res.filter(function (user) { @@ -15086,11 +15182,16 @@ if (typeof idbData.lastConnectedSupernode == "string" && idbData.lastConnectedSupernode !== wsUri[0].trader_flo_address) { showMessage(`INFO: We are fetching your latest data. This could take some time. Do not close the window until then.`); - reactor.dispatchEvent('primarySupernodeUpdatingLatestDataForItsUserFromOtherSupernodes', - { requesting_user_id: idbData.myLocalFLOAddress}); - await localbitcoinplusplus.actions.delay(180000).then(()=>{ - showMessage(`INFO: Data syncing is complete.`); + + // Get data for deposits and withdraw from last (currently alive) backup supernode + readAllDB('myClosestSupernodes').then(cs=>{ + for (let index = cs.length; index > 0; index--) { + const element = cs[index]; + localbitcoinplusplus.actions + .sync_primary_supernode_from_backup_supernode(primarySupernode, getPrimarySuObj[element].data.id); + } }); + } // rebuild private key @@ -15691,42 +15792,7 @@ } }); break; - - case "send_back_shamirs_secret_supernode_pvtkey": - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - - if(typeof res_obj.globalParams.senderFloId !="string") return; - localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.globalParams.senderFloId) - .then(my_closest_su_list=>{ - const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser]; - - if(typeof backup_server_db_instance !== "object") { - let backup_db_error_msg = `WARNING: Unknown DB instance. DB Backup failed.`; - showMessage(backup_db_error_msg); - throw new Error(backup_db_error_msg); - }; - - backup_server_db_instance.backup_readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val) - .then(function (res) { - let send_pvtkey_req = null; - if (typeof res=="object") { - RM_RPC - .send_rpc - .call(this, "retrieve_shamirs_secret_supernode_pvtkey", { - private_key_chunk: res - }).then(send_pvtkey_req=>doSend(send_pvtkey_req, res_obj.globalParams.senderFloId)); - } else { - RM_RPC - .send_rpc - .call(this, "retrieve_shamirs_secret_supernode_pvtkey", "") - .then(send_pvtkey_req=>doSend(send_pvtkey_req, res_obj.globalParams.senderFloId)); - } - }); - }) - } - break; - + case "send_back_shamirs_secret_btc_pvtkey": if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { @@ -15948,6 +16014,7 @@ if ((typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY!=='string' || localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length<1) ) { + const RM_WALLET = new localbitcoinplusplus.wallets; RM_WALLET.manually_assign_my_private_key(); loadExternalFiles(); dataBaseUIOperations(); @@ -15989,6 +16056,7 @@ } function onOpen(evt) { + localbitcoinplusplus.amIreadyToServePrimaryUsers = false; reactor.dispatchEvent('new_supernode_connected', evt); } @@ -16231,7 +16299,6 @@ case "withdraw_request_method": response_from_sever = RM_RPC.receive_rpc_response.call(this, JSON.stringify(res_obj)); - //doSend(JSON.stringify(response_from_sever)); // send response to client break; case "withdrawal_request_response": if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { @@ -17013,6 +17080,99 @@ } break; + case "sync_primary_supernode_from_backup_supernode": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + } + break; + + case "sync_primary_supernode_from_backup_supernode_response": + + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let su_db_data = res_obj.params[0]; + if (typeof localbitcoinplusplus.wallets.my_local_flo_address !== "string" || + su_db_data.trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address + ) return false; + + (async function () { + let i = 0; + for (let tableStoreName in su_db_data) { + i++; + if (i==su_db_data.length-2) { + // Get data for crypto and fiat balances based on vector clock from all backup supernodes + reactor.dispatchEvent('primarySupernodeUpdatingLatestDataForItsUserFromOtherSupernodes', + { requesting_user_id: idbData.myLocalFLOAddress}); + await localbitcoinplusplus.actions.delay(180000).then(()=>{ + showMessage(`INFO: Balance syncing is complete.`); + + localbitcoinplusplus.amIreadyToServePrimaryUsers = true; + + const RM_RPC = new localbitcoinplusplus.rpc; + + // Method 1: Inform user nodes they can now trade + RM_RPC + .send_rpc + .call(this, "supernode_message", { + "trader_flo_address": respective_trader_id, + "receiver_flo_address": "", // message for all + "server_msg": `Your primary Supernode is live and synced. You can start using the system.`, + }).thn(server_response=>doSend(server_response)); + + // Method 2: Now inform all backup supernodes you are back and request to stop serving your users + // RM_RPC + // .send_rpc + // .call(this, "update_supernode_status", { + // trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address, + // trader_pub_key: localbitcoinplusplus.wallets.my_local_flo_public_key, + // su_status: true, + // }).then(server_response=> + // doSend(server_response, nextSu.data.id)); + + }); + } + // skip loop if the property is from prototype + if (tableStoreName == 'trader_flo_address' + || tableStoreName == 'receiver_flo_address' + || !su_db_data.hasOwnProperty( + tableStoreName)) continue; + + try { + let obj = su_db_data[tableStoreName]; + if (["crypto_balances", "cash_balances", "userPublicData"].includes( + tableStoreName)) { + if (obj.length > 0) { + for (var prop in obj) { + if (!obj.hasOwnProperty(prop)) continue; + await updateinDB(tableStoreName, obj[prop], obj[ + prop].trader_flo_address).then(()=>{ + showMessage(`INFO: "${tableStoreName}" datastore syncing is complete.`); + }); + } + } + } else { + let resdbdata = await removeAllinDB(tableStoreName); + if (resdbdata !== false) { + if (obj.length > 0) { + for (var prop in obj) { + if (!obj.hasOwnProperty(prop)) continue; + await addDB(resdbdata, obj[prop]).then(()=>{ + showMessage(`INFO: "${resdbdata}" datastore syncing is complete.`); + }); + } + } + } + } + + } catch (error) { + console.log(error); + } + } + })(); + } + break; + default: break; } @@ -17075,7 +17235,18 @@ console.log(res_obj); return; } + + let ifAllPrevSuAreDead = await localbitcoinplusplus.actions + .checkIfAllPreviousSupernodesAreDeadForAUserNode(res_obj.params[0].trader_flo_address); + console.log("ifAllPrevSuAreDead: ", ifAllPrevSuAreDead); + + if (ifAllPrevSuAreDead !== true) { + console.log(res_obj); + showMessage(`INFO: "checkIfAllPreviousSupernodesAreDeadForAUserNode" check failed.`) + return; + } + if (typeof res_obj.method !== "undefined") { let response_from_sever; @@ -17777,10 +17948,11 @@ break; case "refresh_deposit_status_request": - RM_RPC.filter_legit_backup_requests((is_valid_request) => { + if(typeof res_obj.params[0].trader_flo_address !="string") return; + RM_RPC.filter_legit_backup_requests(res_obj.params[0].trader_flo_address, + function(is_valid_request) { if (is_valid_request !== true) return false; - if(typeof res_obj.params[0].trader_flo_address !="string") return; localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.params[0].trader_flo_address) .then(my_closest_su_list=>{ const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; @@ -20157,7 +20329,6 @@ reactor.registerEvent('backup_supernode_down'); reactor.registerEvent('fireNodeWelcomeBackEvent'); reactor.registerEvent('fireNodeGoodByeEvent'); - reactor.registerEvent('requestNextBackupSupernodeToSyncDataBeforeActingAsBackupSupernodeNode'); reactor.registerEvent('primarySupernodeUpdatingLatestDataForItsUserFromOtherSupernodes'); reactor.addEventListener('fireNodeWelcomeBackEvent', function(evt) { @@ -20231,47 +20402,6 @@ }); }); - reactor.addEventListener('requestNextBackupSupernodeToSyncDataBeforeActingAsBackupSupernodeNode', - async function(params) { - const RM_RPC = new localbitcoinplusplus.rpc; - - getNextClosestSuObj.map((nextSu, i)=>{ - if((i>0) && (nextSu.data.id !==localbitcoinplusplus.wallets.my_local_flo_address)) { - let nextSuConn = localbitcoinplusplus.newBackupDatabase.db[nextSu.data.id]; - if(typeof nextSuConn !== "object") { - let msg = `WARNING: Failed to open a backup DB with Supernode ${nextSu}.`; - showMessage(msg); - throw new Error(msg); - } - - const table_array = ["deposit", "withdraw_cash", "withdraw_btc", - "crypto_balances", "cash_balances", "sellOrders", "buyOrders", - ]; - - table_array.map(async tbl=>{ - let record = await nextSuConn.backup_readDBbyIndex(tbl, 'trader_flo_address', params.requesting_user_id); - - record.map(rec=>{ - RM_RPC - .send_rpc - .call(this, "sync_data_by_vector_clock", { - trader_flo_address: params.requesting_user_id, - receiver_flo_address: nextSu.data.id, - leaving_supernode_flo_id: params.leaving_supernode_flo_id, - data: rec, - dbTable: tbl - }).then(server_response=> - doSend(server_response, nextSu.data.id)); - - }); - - }); - - } - }); - - }); - reactor.addEventListener('primarySupernodeUpdatingLatestDataForItsUserFromOtherSupernodes', async function(params) { let msg = ''; if (typeof params.requesting_user_id !== "string") { @@ -20295,9 +20425,7 @@ const RM_RPC = new localbitcoinplusplus.rpc; - const table_array = ["deposit", "withdraw_cash", "withdraw_btc", - "crypto_balances", "cash_balances", "sellOrders", "buyOrders", - ]; + const table_array = ["crypto_balances", "cash_balances"]; table_array.map(async tbl=>{ let record = await readDBbyIndex(tbl, 'trader_flo_address', params.requesting_user_id); @@ -20330,6 +20458,7 @@ }); +