diff --git a/.gitignore b/.gitignore index de930dc..452463b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -examples.mk json-rpc/ supernode/playground/ playground diff --git a/examples.mk b/examples.mk new file mode 100644 index 0000000..bc7fd8e --- /dev/null +++ b/examples.mk @@ -0,0 +1,36 @@ +SOURCES = $(PROG).c ../mongoose.c +CFLAGS = -g -W -Wall -Werror -I../ -Wno-unused-function $(CFLAGS_EXTRA) $(MODULE_CFLAGS) + +all: $(PROG) + +ifeq ($(OS), Windows_NT) +# TODO(alashkin): enable SSL in Windows +CFLAGS += -lws2_32 +CC = gcc +else +CFLAGS += -pthread +endif + +ifeq ($(SSL_LIB),openssl) +CFLAGS += -DMG_ENABLE_SSL -lssl -lcrypto +endif +ifeq ($(SSL_LIB), krypton) +CFLAGS += -DMG_ENABLE_SSL ../../../krypton/krypton.c -I../../../krypton +endif +ifeq ($(SSL_LIB),mbedtls) +CFLAGS += -DMG_ENABLE_SSL -DMG_SSL_IF=MG_SSL_IF_MBEDTLS -DMG_SSL_MBED_DUMMY_RANDOM -lmbedcrypto -lmbedtls -lmbedx509 +endif + +ifdef ASAN +CC = clang +CFLAGS += -fsanitize=address +endif + +$(PROG): $(SOURCES) + $(CC) $(SOURCES) -o $@ $(CFLAGS) + +$(PROG).exe: $(SOURCES) + cl $(SOURCES) /I../.. /MD /Fe$@ + +clean: + rm -rf *.gc* *.dSYM *.exe *.obj *.o a.out $(PROG) diff --git a/supernode/index.html b/supernode/index.html index ac1830d..5ef635e 100644 --- a/supernode/index.html +++ b/supernode/index.html @@ -9632,9 +9632,12 @@ Object.defineProperty(localbitcoinplusplus, 'server', { value: { - btc_mainnet: "https://blockexplorer.com", - btc_testnet: "https://testnet.blockexplorer.com", - flo_mainnet: "https://livenet.flocha.in", + //btc_mainnet: "https://blockexplorer.com", + btc_mainnet: "https://insight.bitpay.com", + //btc_testnet: "https://testnet.blockexplorer.com", + btc_testnet: "https://test-insight.bitpay.com", + flo_mainnet: "http://flosight.duckdns.org", + //flo_testnet: "http://testnet-flosight.duckdns.org" flo_testnet: "https://testnet.flocha.in" }, writable: false, @@ -10034,6 +10037,7 @@ localbitcoinplusplus.actions = { parse_flo_comments: function (callback) { + return callback('fofof'); if (this.floAddress == null) { return false; } @@ -10048,7 +10052,7 @@ if (request.status >= 200 && request.status < 400) { data.txs.forEach(tx => { - if (typeof tx !== undefined && typeof tx.floData == 'string' && tx.floData + if (typeof tx !== "undefined" && typeof tx.floData == 'string' && tx.floData .length > 0) { callback(tx.floData); } @@ -10068,22 +10072,20 @@ // remove this line later // btcTradeMargin is tolerable difference between Crypto trader should deposit and cryptos he actually deposited RMAssets = - `tradableAsset1=BTC,FLO,BTC_TEST,FLO_TEST#!#tradableAsset2=INR,USD,BTC,FLO,BTC_TEST,FLO_TEST, - #!#validTradingAmount=10000,50000,100000,#!#btcTradeMargin=5000 + `masterFLOPubKey=029EF7838D4D103E62262394B5417E8ABFD75539D19E61CA5FD0C2051B69B29910 + #!#tradableAsset1=BTC,FLO,BTC_TEST,FLO_TEST#!#tradableAsset2=INR,USD, + #!#validTradingAmount=10,50,100,#!#btcTradeMargin=5000 + #!#MaxBackups=2 + #!#miners_fee={"btc":0.0003, "flo":0.0003} #!#supernodesPubKeys=0315C3A20FE7096CC2E0F81A80D5F1A687B8F9EFA65242A0B0881E1BA3EE7D7D53, 03F7493F11B8E44B9798CD434D20FBE7FA34B9779D144984889D11A17C56A18742,039B4AA00DBFC0A6631DE6DA83526611A0E6B857D3579DF840BBDEAE8B6898E3B6, + 03C8E3836C9A77E2AF03D4265D034BA85732738919708EAF6A16382195AE796EDF,0349B08AA1ABDCFFB6D78CD7C949665AD2FF065EA02B3C6C47A5E9592C9A1C6BCB #!#externalFiles={"d3js":"58f54395efa8346e8e94d12609770f66b916897e7f4e05f6c98780cffa5c70a3"}, - #!#ShamirsMaxShares=8#!#supernodeSeeds={"ranchimall1":{"ip":"127.0.0.1","port":"9001","kbucketId":"oZxHcbSf1JC8t5GjutopWYXs7C6Fe9p7ps"}, - "ranchimall2":{"ip":"127.0.0.1","port":"9002","kbucketId":"oTWjPupy3Z7uMdPcu5uXd521HBkcsLuSuM"}, - "ranchimall3":{"ip":"127.0.0.1","port":"9003","kbucketId":"odYA6KagmbokSh9GY7yAfeTUZRtZLwecY1"}}`; - // RMAssets = - // `tradableAsset1=BTC,FLO,BTC_TEST,FLO_TEST#!#tradableAsset2=INR,USD,BTC,FLO,BTC_TEST,FLO_TEST, - // #!#supernodes=127.0.0.1,212.88.88.2#!#MASTER_NODE=023B9F60692A17FAC805D012C5C8ADA3DD19A980A3C5F0D8A5B3500CC54D6E8B75 - // #!#MASTER_RECEIVING_ADDRESS=oVRq2nka1GtALQT8pbuLHAGjqAQ7PAo6uy#!#validTradingAmount=10000,50000,100000,#!#btcTradeMargin=5000 - // #!#supernodesPubKeys=03692E641440795B6279F548C7156775EB624CC8A27FDA94C5E3B8945EC94DE1F1,02F22822D5E887ABBDA3D5E3A51574C547FEAAC00BF01185AA56858D4C9B00334F, - // #!#externalFiles={"d3js":"58f54395efa8346e8e94d12609770f66b916897e7f4e05f6c98780cffa5c70a3"}, - // #!#ShamirsMaxShares=8#!#supernodeSeeds={"ranchimall1":{"ip":"ranchimall1.duckdns.org","port":"9002","kbucketId":"oURVEZQ6sPT8mwDVTGiBVPqJYqjctXYfF3"}, - // "ranchimall2":{"ip":"ranchimall1.duckdns.org","port":"9003","kbucketId":"oapBngvTbcNCSfQfzJ9RS1QYfb4ppSQ9KQ"}}`; + #!#ShamirsMaxShares=8#!#supernodeSeeds={"ranchimall1":{"ip":"127.0.0.1","port":"9111","kbucketId":"oZxHcbSf1JC8t5GjutopWYXs7C6Fe9p7ps"}, + "ranchimall2":{"ip":"127.0.0.1","port":"9112","kbucketId":"oTWjPupy3Z7uMdPcu5uXd521HBkcsLuSuM"}, + "ranchimall3":{"ip":"127.0.0.1","port":"9113","kbucketId":"odYA6KagmbokSh9GY7yAfeTUZRtZLwecY1"}, + "ranchimall4":{"ip":"127.0.0.1","port":"9114","kbucketId":"oJosrve9dBv2Hj2bfncxv2oEpTysg3Wejv"}, + "ranchimall5":{"ip":"127.0.0.1","port":"9115","kbucketId":"oMhv5sAzqg77sYHxmUGZWKRrVo4P4JQduS"}}`; let floAssetsArray = RMAssets.split('#!#'); @@ -10120,23 +10122,206 @@ sync_with_supernode: function (trader_flo_address) { const RM_RPC = new localbitcoinplusplus.rpc; - let sync_request = RM_RPC.send_rpc.call(this, + RM_RPC.send_rpc.call(this, "sync_with_supernode", { "trader_flo_address": trader_flo_address, "job": "SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB", "receiver_flo_address": localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, - }); - doSend(sync_request); + }).then(sync_request=>doSend(sync_request)); + }, + + sync_primary_supernode_from_backup_supernode: async function (primary_su="", backup_su="") { + const RM_RPC = new localbitcoinplusplus.rpc; + + // First check if you yourself have the right data to serve + // If not, either get the data or don't serve the users of + // that dead supernode. + + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", "cash_balances", + "crypto_balances", "buyOrders", "sellOrders", "system_btc_reserves_private_keys"]; + + const su_db_data = await localbitcoinplusplus.actions.get_sharable_db_data(tableArray); + + const dbHashData = await localbitcoinplusplus.actions.getDBTablesLatestHashAndTimestamp(primary_su, su_db_data); + dbHashData['allowed_receivers'] = backup_su; + // Now you have db tables timestamp and tables hashes. Send it to other supernodes to check + // if you have the latest data. If you don't have the latest data, someone + // will send you the latest data which you can verify before updating. + + RM_RPC + .send_rpc + .call(this, "do_you_have_latest_data_for_this_supernode", dbHashData) + .then(server_sync_response=>doSend(server_sync_response)); + + }, + + sync_backup_supernode_from_backup_supernode: async function (requester="", receiversList="", flo_addr_of_backup="") { + const RM_RPC = new localbitcoinplusplus.rpc; + + // First check if you yourself have the right data to serve + // If not, either get the data or don't serve the users of + // that dead supernode. + + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", "cash_balances", + "crypto_balances", "buyOrders", "sellOrders", "system_btc_reserves_private_keys"]; + + const su_db_data = await localbitcoinplusplus.actions.get_sharable_db_data(tableArray, flo_addr_of_backup); + + const dbHashData = await localbitcoinplusplus.actions.getDBTablesLatestHashAndTimestamp(flo_addr_of_backup, su_db_data); + if (typeof receiversList=="object" && receiversList.length>0) { + dbHashData['allowed_receivers'] = receiversList; + } + // Now you have db tables timestamp and tables hashes. Send it to other supernodes to check + // if you have the latest data. If you don't have the latest data, someone + // will send you the latest data which you can verify before updating. + + RM_RPC + .send_rpc + .call(this, "do_you_have_latest_data_for_this_supernode", dbHashData) + .then(server_sync_response=>doSend(server_sync_response)); + }, - get_sharable_db_data: async function (dbTableNamesArray) { + get_sharable_db_data: async function (dbTableNamesArray, backup_db="", filter_deletables=false) { let arr = {}; + let _readAllDB = readAllDB; + 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); + await _readAllDB(elem, filter_deletables).then(e => arr[elem] = e); } return arr; }, + get_sharable_db_data_for_single_user: async function (dbTableNamesArray, backup_db="") { + let arr = {}; + let _readDBbyIndex = readDBbyIndex; + 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]; + _readDBbyIndex = foreign_db.backup_readDBbyIndex.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); + } + return arr; + }, + + getHighestVectorClockInTablesOfaDB: function(dbDataOfSupernode=[]) { + return new Promise((resolve, reject)=>{ + let higestVCList = []; + Object.keys(dbDataOfSupernode).map((m,i)=>{ + if(typeof dbDataOfSupernode[m]=="object") { + arr = dbDataOfSupernode[m].map(d=>{ + if(typeof d !== 'undefined') { + return d.vectorClock; + } + }); + var max = Math.max.apply( null, arr ); + if(typeof max !=="number" || [-Infinity, NaN, false, null, undefined].includes(max)) max=0; + return higestVCList[`${m}_HVC`] = max; + } + }); + resolve(higestVCList); + }); + }, + + getHighestTimestampInTablesOfaDB: function(dbDataOfSupernode=[]) { + return new Promise((resolve, reject)=>{ + let higestVCList = []; + Object.keys(dbDataOfSupernode).map((m,i)=>{ + if(typeof dbDataOfSupernode[m]=="object") { + arr = dbDataOfSupernode[m].map(d=>{ + if(typeof d !== 'undefined') { + return d.timestamp; + } + }); + var max = Math.max.apply( null, arr ); + if(typeof max !=="number" || [-Infinity, NaN, false, null, undefined].includes(max)) max=0; + return higestVCList[`${m}_TIME`] = max; + } + }); + resolve(higestVCList); + }); + }, + + getDBTablesLatestHashAndTimestamp: async function(su=localbitcoinplusplus.wallets.my_local_flo_address, actual_db_data=false, tableArray=false) { + + let get_su = await localbitcoinplusplus.kademlia.determineClosestSupernode(su); + + const supernode_flo_id = get_su[0].data.id; + + if (typeof supernode_flo_id !=="string") { + throw new Error(`WARNING: Failed to calculate supenode of this FLO Id: ${su}.`); + } + + const subjectDB = (supernode_flo_id==localbitcoinplusplus.wallets.my_local_flo_address) ? "":supernode_flo_id; + + let dbDataOfSupernode=actual_db_data; + + if (actual_db_data==false) { + if (typeof tableArray!=="object") { + tableArray = ["deposit", "withdraw_cash", "withdraw_btc", "cash_balances", "crypto_balances", + "buyOrders", "sellOrders", "system_btc_reserves_private_keys"]; + } + dbDataOfSupernode = await localbitcoinplusplus.actions.get_sharable_db_data(tableArray, subjectDB); + } + + if (typeof dbDataOfSupernode=="object" && Object.keys(dbDataOfSupernode).length>0) { + + myArray = {}; + Object.keys(dbDataOfSupernode) + .map(m=>{ + // SORT THE DATA HERE A/C TO TIMESTAMP AND THEN HASH + myArray[m] = Crypto.SHA256(JSON.stringify(dbDataOfSupernode[m])); + }); + + const higestTimestampList = await localbitcoinplusplus.actions.getHighestTimestampInTablesOfaDB(dbDataOfSupernode); + + const dbDataOfSupernodeStr = JSON.stringify(dbDataOfSupernode); + const dbDataOfSupernodeHash = Crypto.SHA256(dbDataOfSupernodeStr); + + myArray["id"] = `SU_DB_${supernode_flo_id}`; + myArray["DBHash"] = dbDataOfSupernodeHash; + myArray["trader_flo_address"] = supernode_flo_id; + myArray["data_of"] = supernode_flo_id; + myArray["higestTimestampList"] = {...higestTimestampList}; + + return myArray; + + } + return false; + }, + + check_if_you_have_latest_dbhash_of_a_db: function(su=localbitcoinplusplus.wallets.my_local_flo_address) { + if (su=localbitcoinplusplus.wallets.my_local_flo_address) { + readDB('supernodesDbHash', `SU_DB_${su}`) + .then(resp=>{ + if (typeof resp=="object") { + RM_RPC + .send_rpc + .call(this, "validate_latest_db_hash", resp) + .then(supernodesDbHash_response=> + console.log(supernodesDbHash_response)); + showMessage(`INFO: Request sent to check latest DB hash for ${su}.`); + } + }); + } + }, + claim_deposit_withdraw: function (claim_id) { if (typeof claim_id == "string" && claim_id.length > 0) { try { @@ -10153,21 +10338,19 @@ localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY); deposit_withdraw_user_claim_obj.userPubKey = localbitcoinplusplus.wallets.my_local_flo_public_key; + deposit_withdraw_user_claim_obj.trader_flo_address = localbitcoinplusplus.wallets.my_; deposit_withdraw_user_claim_obj.hash = deposit_withdraw_user_claim_hash; deposit_withdraw_user_claim_obj.sign = deposit_withdraw_user_claim_sign; deposit_withdraw_user_claim_obj.receiver_flo_address = localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS; - let deposit_withdraw_claim = - RM_RPC - .send_rpc - .call( - this, + RM_RPC + .send_rpc + .call( + this, "deposit_withdraw_user_claim", deposit_withdraw_user_claim_obj - ); - - doSend(deposit_withdraw_claim); + ).then(deposit_withdraw_claim=>doSend(deposit_withdraw_claim)); } catch (error) { console.log(error); @@ -10191,7 +10374,8 @@ if (localbitcoinplusplus.wallets.my_local_flo_address==wd.trader_flo_address) { action_req += `
Please click the button below only if you received the cash.
`; action_req += ``; - } else if(localbitcoinplusplus.wallets.my_local_flo_address==wd.depositor_flo_id) { + } else if(localbitcoinplusplus.wallets.my_local_flo_address==wd.depositor_flo_id + && wdf.status!==3) { action_req += `Please click the button below only if you actually deposited the money. Any fake claim can cause a heavy penalty.
`; action_req += ``; @@ -10229,6 +10413,47 @@ 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`); + reject(`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("", localbitcoinplusplus.master_configurations.MaxBackups, + 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)=> + (typeof su_status[0] !== "object" || su_status[0].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)); + }); + } } @@ -10245,14 +10470,38 @@ launchKBucket: function(flo_id) { return new Promise((resolve, reject)=>{ try { - const KBucketId = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', flo_id) + const KBucketId = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', flo_id); const kbOptions = { localNodeId: KBucketId } window.KBucket = new BuildKBucket(kbOptions); - window.supernodeKBucket = new BuildKBucket(kbOptions); + + const master_flo_pubKey = localbitcoinplusplus.master_configurations.masterFLOPubKey; + const master_flo_addr = bitjs.FLO_TEST.pubkey2address(master_flo_pubKey); + if(typeof master_flo_pubKey !== "string") return reject(false); + const SuKBucketId = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', master_flo_addr); + const SukbOptions = { localNodeId: SuKBucketId } + window.supernodeKBucket = new BuildKBucket(SukbOptions); + resolve(true); } catch (error) { reject(error); } + }); + }, + launchSupernodesKBucket: function() { + + localbitcoinplusplus.master_configurations.supernodesPubKeys.map(pubKey=>{ + return new Promise((resolve, reject)=>{ + try { + let flo_id = bitjs.FLO_TEST.pubkey2address(pubKey); + let kname = `SKBucket_${pubKey}`; + const KBucketId = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', flo_id) + const kbOptions = { localNodeId: KBucketId } + window[kname] = new BuildKBucket(kbOptions); + resolve(true); + } catch (error) { + reject(error); + } + }) }) }, addContact: function (id, data, KB=KBucket) { @@ -10273,15 +10522,20 @@ return {decodedId:decodedId, data:data}; }, addNewUserNodeInKbucketAndDB: function (blockchain, address, data, KB=KBucket) { + let closestSupernodePubKey = localbitcoinplusplus.master_configurations + .supernodesPubKeys.filter(j=>bitjs.FLO_TEST.pubkey2address(j)==data.id); + let kbuck = this.addNewUserNodeInKbucket(blockchain, address, data, KB); readDB('kBucketStore', kbuck.decodedId).then(kres=>{ if (typeof kres=="object") { kres.data = data; + kres.primary_supernode_flo_public_key = closestSupernodePubKey[0]; } else { kbuckObj={ id: kbuck.decodedId, vectorClock: 0, data: kbuck.data, + primary_supernode_flo_public_key: closestSupernodePubKey[0], last_updated_on: + new Date(), } kres = kbuckObj; @@ -10292,6 +10546,20 @@ return Promise.resolve(kbuck); }); }, + addFullKBDataInKBucketAndDB: function(full_data, KB) { + let userKBId = ''; + let isFloIdUint8 = full_data.id instanceof Uint8Array; + if (!isFloIdUint8) { + userKBId = this.floIdToKbucketId('FLO_TEST', full_data.data.id); + this.addContact(userKBId, full_data.data, KB); + full_data.id = userKBId; + } else { + this.addContact(full_data.id, full_data.data, KB); + } + updateinDB('kBucketStore', full_data) + .then(ms=>showMessage(`INFO: Added/Updated a node in DB.`)) + .catch(e=>{showMessage(`ERROR: Failed to add a node in DB.`)}); + }, floIdToKbucketId: function (blockchain, address) { const decodedId = this.decodeBase58Address(blockchain, address); const nodeIdBigInt = new BigInteger(decodedId, 16); @@ -10347,6 +10615,8 @@ let kbuck = this.addNewUserNodeInKbucket("FLO_TEST", seedObj[1].kbucketId, { id: seedObj[1].kbucketId }, supernodeKBucket); }); + + resolve(true); }) }, updateClosestSupernodeSeeds: function(flo_addr) { @@ -10359,7 +10629,7 @@ ip: nearestSupernodeAddress.ip, port: nearestSupernodeAddress.port, trader_flo_address: nearestSupernodeAddress.kbucketId, - is_live: true + is_live: null }).then(updatedClosestSupernodes=>{ readAllDB('myClosestSupernodes').then(nearestSupernodeAddresslist=>{ showMessage(`INFO: Updated closest supernodes list successfully.`); @@ -10375,7 +10645,6 @@ if (nearestSupernodeAddresslist.length<1) { nearestSupernodeAddresslist = await this.updateClosestSupernodeSeeds(flo_addr); } - //this.restoreSupernodeKBucket(flo_addr, "FLO_TEST", supernodeKBucket); resolve(nearestSupernodeAddresslist); }); }, @@ -10390,22 +10659,40 @@ } }); }, - determineClosestSupernode: function(flo_addr, KB=supernodeKBucket, n=1) { + determineClosestSupernode: function(flo_addr="", n=1, KB=supernodeKBucket, su="") { return new Promise((resolve, reject)=>{ let msg = ``; - if (typeof KB !== "object") { + if (typeof supernodeKBucket !== "object") { msg = `ERROR: Supernode KBucket not found.`; showMessage(msg); reject(msg); return false; } + + if (su.length>0) { + try { + let closestSupernodeMasterList = supernodeKBucket.closest(supernodeKBucket.localNodeId); + const index = closestSupernodeMasterList.findIndex(f=>f.data.id==su); + let tail = closestSupernodeMasterList.splice(0, index); + const newClosestSupernodeMasterList = closestSupernodeMasterList.concat(tail); + resolve(newClosestSupernodeMasterList); + return true; + } catch (error) { + reject(error); + } + return false; + } try { + if(flo_addr.length < 0) { + showMessage(`WARNING: No Flo Id provided to determine closest Supenode.`); + return; + } let isFloIdUint8 = flo_addr instanceof Uint8Array; if (!isFloIdUint8) { flo_addr = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', flo_addr); } - const closestSupernode = KB.closest(flo_addr, n); + const closestSupernode = supernodeKBucket.closest(flo_addr, n); resolve(closestSupernode); return true; } catch (error) { @@ -10416,8 +10703,8 @@ }) }, - addClosestSupernodeInDB: function(flo_addr, KB=KBucket) { - return new Promise((resolve, reject)=>{ + addClosestSupernodeInDB: function(flo_addr, KB=supernodeKBucket) { + return new Promise(async (resolve, reject)=>{ const supernodeSeeds = localbitcoinplusplus.master_configurations.supernodeSeeds; if (typeof supernodeSeeds !== "object") reject("Failed to get supernode seeds."); let supernodeSeedsObj = JSON.parse(supernodeSeeds); @@ -10429,9 +10716,8 @@ { id: seedObj[1].kbucketId }); }); - let currentNodeBucketId = localbitcoinplusplus.kademlia - .floIdToKbucketId("FLO_TEST", flo_addr); - let nearestSupernode = KB.closest(currentNodeBucketId, 4); + let primarySu = await this.determineClosestSupernode(flo_addr); + let nearestSupernode = await this.determineClosestSupernode(flo_addr="", n=1, supernodeKBucket, primarySu[0].data.id); let nearestSupernodeIds = nearestSupernode.map(f=>f.data.id); let supernodeSeedsArray = Object.values(supernodeSeedsObj) .filter(seed=>nearestSupernodeIds.includes(seed.kbucketId)) @@ -10595,25 +10881,544 @@ pubKey = res.trader_flo_pubKey; let data = localbitcoinplusplus.encrypt.encryptMessage(message, pubKey); const RM_RPC = new localbitcoinplusplus.rpc; - let bar = RM_RPC + RM_RPC .send_rpc - .call(this, rpc_subject, {data: data, receiver_flo_address: flo_id}); - doSend(bar); + .call(this, rpc_subject, {data: data, + receiver_flo_address: flo_id, + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address + }).then(bar=>doSend(bar)); }); }, transmitMessageToMiddleMan: function (dataToBeSentToReceiver, receiverFloAddress) { const RM_RPC = new localbitcoinplusplus.rpc; dataToBeSentToReceiver.sender_flo_address = localbitcoinplusplus.wallets.my_local_flo_address; - let bar = RM_RPC + dataToBeSentToReceiver.trader_flo_address = localbitcoinplusplus.wallets.my_local_flo_address + RM_RPC .send_rpc - .call(this, "MessageForMiddleman", dataToBeSentToReceiver); - doSend(bar); + .call(this, "MessageForMiddleman", dataToBeSentToReceiver) + .then(bar=>doSend(bar)); } } + + + @@ -11044,19 +11859,111 @@ Rpc.prototype = { send_rpc(method, ...params) { - var request = new JSON_RPC.Request(method, params); - var id = request.id; - this.rpc_req_id = id; - if (typeof localbitcoinplusplus.wallets.my_local_flo_address == "string") { - request.globalParams.senderFloId = localbitcoinplusplus.wallets.my_local_flo_address; - } - - if (typeof params[0].receiver_flo_address == "string") { - request.globalParams.receiverFloId = params[0].receiver_flo_address; - } + return new Promise((resolve, reject)=>{ + var request = new JSON_RPC.Request(method, params); + var id = request.id; + this.rpc_req_id = id; - return request.toString(); + (async function(request) { + const all_receivers_methods = ['do_you_have_latest_data_for_this_supernode', + 'sync_backup_supernode_from_backup_supernode']; + + if (typeof localbitcoinplusplus.wallets.my_local_flo_address == "string") { + request.globalParams.senderFloId = localbitcoinplusplus.wallets.my_local_flo_address; + + const mySupernodeObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(localbitcoinplusplus.wallets.my_local_flo_address); + + request.globalParams.primarySupernode = mySupernodeObj[0].data.id; + + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + //const my_closest_su = await localbitcoinplusplus.kademlia + //.determineClosestSupernode('', '', supernodeKBucket, localbitcoinplusplus.wallets.my_local_flo_address); + + const my_closest_su = await readAllDB('myClosestSupernodes'); + + if (typeof my_closest_su=="object") { + if (!all_receivers_methods.includes(method)) { + request.globalParams["receiversList"] = []; + let live_nodes = 0; + for (let j = 1; j <= my_closest_su.length-1; j++) { + if(typeof my_closest_su[j].trader_flo_address !== "string") continue; + if(my_closest_su[j].is_live==true) { + live_nodes++; + } + request.globalParams.receiversList.push(my_closest_su[j].trader_flo_address); + /* If a closestNode is dead include it but also include an extra node + (preferrably live) after him. Idea is that message should reach + closest 'live' supernode. */ + if (live_nodes==localbitcoinplusplus.master_configurations.MaxBackups) break; + } + } + + } + } else { + if (!all_receivers_methods.includes(method)) { + request.globalParams["receiversList"] = []; + request.globalParams.receiversList.push(localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS); + } + } + } else { + const resObj = await readDB('localbitcoinUser', '00-01'); + if(typeof resObj !== "object" || typeof resObj.myLocalFLOAddress !== "string") return; + request.globalParams.senderFloId = resObj.myLocalFLOAddress; + + const mySupernodeObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(resObj.myLocalFLOAddress); + + request.globalParams.primarySupernode = mySupernodeObj[0].data.id; + + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(resObj.myLocalFLOPublicKey)) { + //const my_closest_su = await localbitcoinplusplus.kademlia + //.determineClosestSupernode('', '', supernodeKBucket, resObj.myLocalFLOAddress); + const my_closest_su = await readAllDB('myClosestSupernodes'); + + if (typeof my_closest_su=="object") { + if (!all_receivers_methods.includes(method)) { + request.globalParams["receiversList"] = []; + let live_nodes = 0; + for (let j = 1; j <= my_closest_su.length-1; j++) { + if(typeof my_closest_su[j].trader_flo_address !== "string") continue; + if(my_closest_su[j].is_live==true) { + live_nodes++; + } + request.globalParams.receiversList.push(my_closest_su[j].trader_flo_address); + /* If a closestNode is dead include it but also include an extra node + (preferrably live) after him. Idea is that message should reach + closest 'live' supernode. */ + if (live_nodes==localbitcoinplusplus.master_configurations.MaxBackups) break; + } + } + } + } else { + if (!all_receivers_methods.includes(method)) { + request.globalParams["receiversList"] = []; + request.globalParams.receiversList.push(localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS); + } + } + } + + if (typeof params[0].receiver_flo_address == "string") { + //request.globalParams.receiverFloId = params[0].receiver_flo_address; + if (typeof request.globalParams.receiversList == "object") { + if(typeof request.globalParams["receiversList"] !== "object") request.globalParams["receiversList"] = []; + if (!request.globalParams.receiversList.includes(params[0].receiver_flo_address)) { + request.globalParams.receiversList.push(params[0].receiver_flo_address); + } + } + } + + return resolve(request.toString()); + + })(request); + }) + }, filter_legit_requests: function (flo_id=null, callback) { @@ -11068,16 +11975,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 { @@ -11124,24 +12032,30 @@ request.response = {}; let err_msg; + let recvr_flo_id = params.receiver_flo_address || request.globalParams.receiverFloId; + if (typeof recvr_flo_id == "string" + && recvr_flo_id.length > 0 + && recvr_flo_id !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + if (method=="sync_with_supernode") { RM_RPC.filter_legit_requests(params.trader_flo_address, function (is_valid_request) { if (is_valid_request === true && params.job == "SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB" && params.trader_flo_address.length > 0) { const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", - "crypto_balances", "cash_balances", "userPublicData" + "crypto_balances", "cash_balances", "userPublicData", + "buyOrders", "sellOrders" ]; localbitcoinplusplus.actions.get_sharable_db_data(tableArray).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; - let server_sync_response = RM_RPC + RM_RPC .send_rpc - .call(this, "server_sync_response", - su_db_data); - doSend(server_sync_response); + .call(this, "server_sync_response", su_db_data) + .then(server_sync_response=>doSend(server_sync_response)) } }); } @@ -11155,8 +12069,9 @@ // CHECK HERE IF USER IS INDULGED IN ANY MORE TRADE. IF TRUE RETURN ERROR await readAllDB("deposit").then(function (res) { if (typeof res == "object" && res.length > 0) { - let canUserTrade = res.map(function (user) { - return respective_trader_id == user.trader_flo_address; + let canUserTrade = res.filter(function (user) { + return (respective_trader_id == user.trader_flo_address + && user.status==1); }); if (canUserTrade.includes(true)) { request.response = @@ -11164,14 +12079,13 @@ You must finish your previous pending orders to qualify again to trade.`; const RM_RPC = new localbitcoinplusplus.rpc; - let server_response = RM_RPC + RM_RPC .send_rpc .call(this, "supernode_message", { - "trader_flo_id": respective_trader_id, + "trader_flo_address": respective_trader_id, "receiver_flo_address": respective_trader_id, "server_msg": request.response - }); - doSend(server_response); + }).then(server_response=>doSend(server_response)); showMessage(request.response); throw new Error(request.response); return false; @@ -11191,14 +12105,14 @@ You must finish your previous pending deposit/withdraw action to qualify again to trade.`; const RM_RPC = new localbitcoinplusplus.rpc; - let server_response = RM_RPC + RM_RPC .send_rpc .call(this, "supernode_message", { - "trader_flo_id": respective_trader_id, + "trader_flo_address": respective_trader_id, "receiver_flo_address": respective_trader_id, "server_msg": server_msg - }); - doSend(server_response); + }).then(server_response=>doSend(server_response)); + showMessage(server_msg); throw new Error( "User has not finished previous pending actions." @@ -11230,12 +12144,13 @@ function (supernode_signed_res) { supernode_signed_res.receiver_flo_address = params.trader_flo_address; if (typeof supernode_signed_res == "object") { - let buy_request_response = RM_RPC + RM_RPC .send_rpc .call(this, "trade_buy_request_response", - supernode_signed_res); - doSend(buy_request_response); + supernode_signed_res) + .then(buy_request_response=>doSend(buy_request_response)); + // Init trading RM_TRADE.createTradePipes(params.currency); return true; @@ -11263,7 +12178,7 @@ params.trader_flo_address, params.product, params.currency); if (typeof trade_margin.remaining_crypto_credit == "number" && typeof trade_margin.remaining_fiat_credit == "number") { - let eqCrypto = RM_TRADE.calculateCryptoEquivalentOfCash(params.buy_price); + let eqCrypto = RM_TRADE.calculateCryptoEquivalentOfCash(params.buy_price, params.currency, params.product); if (trade_margin.remaining_crypto_credit > 0 && trade_margin.remaining_crypto_credit >= eqCrypto) { request.response = RM_TRADE.trade_sell.call( @@ -11271,12 +12186,13 @@ function (supernode_signed_res) { if (typeof supernode_signed_res == "object") { supernode_signed_res.receiver_flo_address = params.trader_flo_address; - let sell_request_response = RM_RPC + RM_RPC .send_rpc .call(this, "trade_sell_request_response", - supernode_signed_res); - doSend(sell_request_response); + supernode_signed_res) + .then(sell_request_response=>doSend(sell_request_response)); + // Init trading RM_TRADE.createTradePipes(params.currency); return true; @@ -11306,20 +12222,16 @@ if (is_valid_request !== true) return false; // This code will only run for supernodes - if (typeof params.product !== "undefined" && - (localbitcoinplusplus.master_configurations.tradableAsset1.includes( - params.product) || - localbitcoinplusplus.master_configurations.tradableAsset2.includes( - params.product)) && - typeof params.depositing_amount !== "undefined" && - localbitcoinplusplus.master_configurations.tradableAsset2.includes( - params.currency) && - typeof localbitcoinplusplus.master_configurations.validTradingAmount !== - 'undefined' && - localbitcoinplusplus.master_configurations.validTradingAmount.includes( - parseFloat(params.depositing_amount)) && - typeof params.trader_flo_address == "string" && - params.trader_flo_address.length > 0 + if (typeof params.product !== "undefined" + && (localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product) + || localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.product)) + && typeof params.depositing_amount !== "undefined" + && localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency) + && typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' + && localbitcoinplusplus.master_configurations.validTradingAmount + .includes(parseFloat(params.depositing_amount)) + && typeof params.trader_flo_address == "string" + && params.trader_flo_address.length > 0 ) { RM_WALLET.getUserPublicKey(params.trader_flo_address, async function (requester_public_key) { @@ -11340,10 +12252,11 @@ let generate_btc_keys_for_requester = RM_WALLET .generateFloKeys(null, params.product); + console.table(generate_btc_keys_for_requester); + params.id = helper_functions.unique_id(); params.status = 1; - params.btc_address = - generate_btc_keys_for_requester.address; + params.btc_address = generate_btc_keys_for_requester.address; params.bitcoinToBePaid = RM_TRADE.calculateCryptoEquivalentOfCash( params.depositing_amount, params.currency, @@ -11359,7 +12272,7 @@ } readDB("localbitcoinUser", "00-01").then( - function (su_data) { + async function (su_data) { if (typeof su_data == "object" && typeof su_data.myLocalFLOPublicKey == "string" && @@ -11369,7 +12282,6 @@ .supernodesPubKeys .includes(su_data.myLocalFLOPublicKey) ) { - let receivedTradeInfoHash = Crypto.SHA256(JSON.stringify(receivedTradeInfo)); @@ -11381,37 +12293,31 @@ receivedTradeInfo["order_validator_public_key"] = su_data.myLocalFLOPublicKey; try { - const this_btc_pvt_key = - generate_btc_keys_for_requester.privateKeyWIF; - const this_btc_tx_key = - Crypto.util.randomBytes(64); - const - this_btc_pvt_key_shamirs_secret = + const this_btc_pvt_key = generate_btc_keys_for_requester.privateKeyWIF; + const this_btc_tx_key = Crypto.util.randomBytes(64); + const this_btc_pvt_key_shamirs_secret = RM_WALLET.createShamirsSecretShares(this_btc_pvt_key, 10, 5); - if (typeof this_btc_pvt_key_shamirs_secret == - "object" && - this_btc_pvt_key_shamirs_secret - .length > 0) { - addDB("deposit", receivedTradeInfo); + if (typeof this_btc_pvt_key_shamirs_secret == "object" + && this_btc_pvt_key_shamirs_secret.length > 0) { + + const deposit_res = await addDB("deposit", receivedTradeInfo); // Send the address to the requester - let - deposit_response_object = { - error: false, - method: "deposit_asset_request_response", - msg: `Please send ${params.product} ${params.bitcoinToBePaid} to the following addres: ${generate_btc_keys_for_requester.address}.`, - data: receivedTradeInfo - }; + let deposit_response_object = { + error: false, + method: "deposit_asset_request_response", + msg: `Please send ${params.product} ${params.bitcoinToBePaid} to the following addres: ${generate_btc_keys_for_requester.address}.`, + data: deposit_res + }; deposit_response_object.receiver_flo_address = params.trader_flo_address; - let - deposit_request_response = - RM_RPC + deposit_response_object.trader_flo_address = params.trader_flo_address; + RM_RPC .send_rpc .call(this, "deposit_asset_request_response", deposit_response_object - ); - doSend(deposit_request_response); + ).then(deposit_request_response=> + doSend(deposit_request_response)); let this_btc_pvt_key_shamirs_secret_array = @@ -11434,7 +12340,8 @@ .encrypt( chunks, this_btc_tx_key - ) + ), + "trader_flo_address": params.trader_flo_address }; return chunk_array; }); @@ -11442,16 +12349,13 @@ // Send chunks of private keys to other supernodes this_btc_pvt_key_shamirs_secret_array .map(shares => { - let - store_pvtkey_req = - RM_RPC + RM_RPC .send_rpc .call( this, "store_shamirs_secret_pvtkey_shares", shares - ); - doSend(store_pvtkey_req); + ).then(store_pvtkey_req=>doSend(store_pvtkey_req)); }); if (typeof localbitcoinplusplus @@ -11483,22 +12387,21 @@ this_btc_tx_key; const system_btc_reserves_private_keys_object = { - id: helper_functions - .unique_id(), - product: params - .product, - btc_address: params - .btc_address, + id: helper_functions.unique_id(), + product: params.product, + btc_address: params.btc_address, balance: null, - trader_flo_address: params - .trader_flo_address, + trader_flo_address: params.trader_flo_address, btc_private_key_shamirs_id: btc_private_key_shamirs_id, supernode_transaction_key: supernode_transaction_key } addDB( "system_btc_reserves_private_keys", system_btc_reserves_private_keys_object - ); + ).then(pk_res=>{ + // Send it to neighbor nodes to store as backup + reactor.dispatchEvent('store_backup_crypto_pk_object', pk_res); + }); } catch (error) { throw new Error( error); } @@ -11510,21 +12413,21 @@ } // Send the address to the requester - let deposit_response_object = { - error: false, - method: "deposit_asset_request_response", - msg: `Please send the ${params.product} to ${generate_btc_keys_for_requester.address}.`, - data: receivedTradeInfo, - receiver_flo_address: params.trader_flo_address, - }; + // let deposit_response_object = { + // error: false, + // method: "deposit_asset_request_response", + // msg: `Please send the ${params.product} to ${generate_btc_keys_for_requester.address}.`, + // data: receivedTradeInfo, + // receiver_flo_address: params.trader_flo_address, + // trader_flo_address: params.trader_flo_address, + // }; - let deposit_request_response = - RM_RPC.send_rpc - .call(this, - "deposit_asset_request_response", - deposit_response_object - ); - doSend(deposit_request_response); + // RM_RPC.send_rpc + // .call(this, + // "deposit_asset_request_response", + // deposit_response_object + // ).then(deposit_request_response=> + // doSend(deposit_request_response)); return true; } }); @@ -11554,11 +12457,9 @@ Crypto.SHA256(JSON.stringify( receivedTradeInfo)); - receivedTradeInfo[ - "depositDataHash"] = + receivedTradeInfo["depositDataHash"] = receivedTradeInfoHash; - receivedTradeInfo[ - "order_validator_sign"] = + receivedTradeInfo["order_validator_sign"] = RM_WALLET.sign( receivedTradeInfoHash, localbitcoinplusplus.wallets @@ -11570,12 +12471,11 @@ // YOU NEED TO DETERMINE A BANK ACCOUNT HERE IF NO ONE IS WITHDRAWING try { - addDB("deposit", - receivedTradeInfo); + addDB("deposit", receivedTradeInfo); readDBbyIndex( "withdraw_cash", "status", 1).then( - function ( + async function ( withdrawers_list ) { if (typeof withdrawers_list == @@ -11585,7 +12485,7 @@ withdrawers_list.filter( wd => wd.currency == params.currency).map( - function (withdrawer) { + async function (withdrawer) { if ( withdrawer.withdraw_amount == params.depositing_amount && @@ -11595,14 +12495,13 @@ withdrawer.status = 2; // A depositor has been asked to deposit money withdrawer.depositor_found_at = + new Date(); withdrawer.depositor_flo_id = receivedTradeInfo.trader_flo_address; - updateinDB ("withdraw_cash", withdrawer, withdrawer.trader_flo_address); + const withdraw_resp = await updateinDB ("withdraw_cash", withdrawer, withdrawer.trader_flo_address); receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account - updateinDB( + const receivedTradeInfo_resp = await updateinDB( "deposit", receivedTradeInfo, - receivedTradeInfo - .trader_flo_address + receivedTradeInfo.trader_flo_address ); let withdrawer_bank_account = withdrawer.receivinAddress; @@ -11610,21 +12509,19 @@ let deposit_response_object = { error: false, method: "deposit_asset_request_response", - msg: `Plese send the money to following bank address: "${withdrawer_bank_account}"`, - data: receivedTradeInfo, - withdrawer_data: withdrawer, + msg: `Please send the money to following bank address: "${withdrawer_bank_account}"`, + data: receivedTradeInfo_resp, + withdrawer_data: withdraw_resp, receiver_flo_address: receivedTradeInfo.trader_flo_address, + trader_flo_address: receivedTradeInfo.trader_flo_address, }; - let - deposit_request_response = - RM_RPC.send_rpc.call( + RM_RPC.send_rpc.call( this, "deposit_asset_request_response", deposit_response_object - ); - - doSend(deposit_request_response); + ).then(deposit_request_response=> + doSend(deposit_request_response)); return true; } else { err_msg = "Deposit request failed: We could not find a withdrawer."; @@ -11635,26 +12532,9 @@ ); } else { //No one is withdrawing so provide your bank details - let - deposit_response_object = { - error: false, - method: "deposit_asset_request_response", - msg: `Plese send the money to following bank address: "System determined bank".`, - data: receivedTradeInfo, - receiver_flo_address: receivedTradeInfo.trader_flo_address, - }; - let - deposit_request_response = - RM_RPC - .send_rpc - .call( - this, - "deposit_asset_request_response", - deposit_response_object - ); - + receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account - updateinDB + const receivedTradeInfo_resp = await updateinDB ( "deposit", receivedTradeInfo, @@ -11662,7 +12542,23 @@ .trader_flo_address ); - doSend(deposit_request_response); + let deposit_response_object = { + error: false, + method: "deposit_asset_request_response", + msg: `Please send the money to following bank address: "System determined bank".`, + data: receivedTradeInfo_resp, + receiver_flo_address: receivedTradeInfo.trader_flo_address, + trader_flo_address: receivedTradeInfo.trader_flo_address, + }; + + RM_RPC + .send_rpc + .call( + this, + "deposit_asset_request_response", + deposit_response_object + ).then(deposit_request_response=> + doSend(deposit_request_response)); return true; } } @@ -11690,41 +12586,40 @@ if (is_valid_request !== true) return false; - if (typeof params.product !== "undefined" && - (localbitcoinplusplus.master_configurations.tradableAsset1.includes( - params.product) || - localbitcoinplusplus.master_configurations.tradableAsset2.includes( - params.currency)) && - typeof params.withdrawing_amount !== "undefined" && - typeof localbitcoinplusplus.master_configurations.validTradingAmount !== - 'undefined' && - localbitcoinplusplus.master_configurations.validTradingAmount.includes( - parseFloat(params.withdrawing_amount)) && - typeof params.trader_flo_address == "string" && params.trader_flo_address - .length > 0 && - typeof params.receivinAddress == "string" && params.receivinAddress - .length > - 0 && typeof params.currency == "string" + if (typeof params.product !== "undefined" + && ((localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product) + || localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.product)) + && localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency)) + && typeof params.withdrawing_amount !== "undefined" + && typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' + && typeof params.trader_flo_address == "string" + && params.trader_flo_address.length > 0 + && typeof params.receivinAddress == "string" + && params.receivinAddress.length > 0 + && typeof params.currency == "string" ) { + const get_requester_primary_supernode = await localbitcoinplusplus.kademlia + .determineClosestSupernode(params.trader_flo_address); + const primarySupernodeForThisUser = get_requester_primary_supernode[0].data.id; + await RM_TRADE.resolve_current_crypto_price_in_fiat(params.product, params.currency); let trade_margin = await RM_TRADE.getAssetTradeAndWithdrawLimit( params.trader_flo_address, params.product, params.currency ); - if (localbitcoinplusplus.master_configurations.tradableAsset1.includes( - params.product)) { - let eqCrypto = RM_TRADE.calculateCryptoEquivalentOfCash( - params.withdrawing_amount); - if (trade_margin.remaining_crypto_credit < 0 && - trade_margin.remaining_crypto_credit < eqCrypto) { + if (localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product)) { + if (trade_margin.remaining_crypto_credit < 0 + || params.withdrawing_amount <= 0 + || trade_margin.remaining_crypto_credit < params.withdrawing_amount) { err_msg = `Insufficient crypto balance to withdraw. You can withdraw upto: ${params.product} ${trade_margin.remaining_crypto_credit}` showMessage(err_msg); throw new Error(err_msg); } } else { - if (trade_margin.remaining_fiat_credit < 0 && trade_margin.remaining_fiat_credit < - params.withdrawing_amount) { + if (trade_margin.remaining_fiat_credit < 0 + || params.withdrawing_amount <= 0 + || trade_margin.remaining_fiat_credit < params.withdrawing_amount) { err_msg = `Insufficient fiat balance to withdraw. You can withdraw upto: ${params.currency} ${trade_margin.remaining_fiat_credit}`; showMessage(err_msg); throw new Error(err_msg); @@ -11744,26 +12639,13 @@ typeof btc_balance_res .trader_flo_address == "string" && btc_balance_res.crypto_balance > 0) { - let withdrawer_btc_balance = parseFloat( - btc_balance_res.crypto_balance); - let withdrawing_btc_amount_in_cash = - parseFloat(params.withdrawing_amount); - if (!localbitcoinplusplus.master_configurations - .tradableAsset2.includes(params.currency) - ) { - err_msg = "Invalid or unsupported currency."; - showMessage(err_msg); - throw new Error(err_msg); - } - let eqBTC = RM_TRADE.calculateCryptoEquivalentOfCash( - withdrawing_btc_amount_in_cash, - params.currency, params.product); - eqBTC = parseFloat(eqBTC).toFixed(8); + let withdrawer_btc_balance = Number( + btc_balance_res.crypto_balance).toFixed(8); + const eqBTC = Number(parseFloat(params.withdrawing_amount).toFixed(8)); let withdrawer_new_btc_balance = withdrawer_btc_balance - eqBTC; - if (withdrawer_new_btc_balance > 0 && + if (withdrawer_new_btc_balance >= 0 && withdrawer_btc_balance > 0 && - withdrawing_btc_amount_in_cash > 0 && eqBTC > 0 && eqBTC <= withdrawer_btc_balance) { @@ -11775,8 +12657,7 @@ ****************************************************************************/ let sum_total_btc = 0; let valid_utxo_list = []; - let receiverBTCAddress = params.receivinAddress - .trim(); + let receiverBTCAddress = params.receivinAddress.trim(); readAllDB("deposit").then(function ( deposit_list) { @@ -11830,9 +12711,8 @@ let valid_btc_list = valid_utxo_list.map( deposit_arr => { - deposit_arr - .status = - 3 // Deposited Bitcoin is under process + // Deposited Bitcoin is under process + deposit_arr.status = 3; updateinDB( "deposit", deposit_arr, @@ -11854,13 +12734,10 @@ .btc_address, receiverBTCAddress: params .receivinAddress, - receiverBTCEquivalentInCash: withdrawing_btc_amount_in_cash, - currency: params - .currency, - product: params - .product, - change_adress: deposit_arr - .btc_address, + receivingBTC: eqBTC, + currency: params.currency, + product: params.product, + change_adress: deposit_arr.btc_address, timestamp: + new Date() } addDB( @@ -11869,8 +12746,7 @@ ); return { withdraw_id: withdraw_id, - deposited_btc_address: deposit_arr - .btc_address + deposited_btc_address: deposit_arr.btc_address }; }); @@ -11880,41 +12756,26 @@ ( 'system_btc_reserves_private_keys', 'btc_address', - vbl - .deposited_btc_address + vbl.deposited_btc_address ).then( - function ( - res - ) { - let - retrieve_pvtkey_req_id = - res[ - 0 - ] - .id; - res - [ - 0 - ] + function (res) { + let retrieve_pvtkey_req_id = res[0].id; + res[0] .btc_private_key_shamirs_id .map( bpks => { - let - retrieve_pvtkey_req = - RM_RPC + RM_RPC .send_rpc .call( this, "send_back_shamirs_secret_btc_pvtkey", { retrieve_pvtkey_req_id: retrieve_pvtkey_req_id, chunk_val: bpks, - withdraw_id: vbl.withdraw_id + withdraw_id: vbl.withdraw_id, + db_inst: primarySupernodeForThisUser } - ); - doSend - ( - retrieve_pvtkey_req - ); + ).then(retrieve_pvtkey_req=>doSend(retrieve_pvtkey_req)); + } ); }); @@ -11950,8 +12811,15 @@ }; } }); - } else if (!localbitcoinplusplus.master_configurations.tradableAsset1 + } else if (localbitcoinplusplus.master_configurations.tradableAsset2 .includes(params.product)) { + + if (!localbitcoinplusplus.master_configurations.validTradingAmount + .includes(parseFloat(params.withdrawing_amount))) { + err_msg = `Withdrawal request failed: Please enter valid fiat amount.`; + showMessage(err_msg); + throw new Error(err_msg); + } // Check if there's no already a withdraw cash order of this user /*ONLY DELETE A WITHDRAW ORDER WHEN A DEPOSITOR HAS CONFIRMED DEPOSIT AND RECEIVER HAS CONFIRMED WITHDRAW*/ @@ -12030,20 +12898,20 @@ addDB( "withdraw_cash", withdraw_request_db_object - ); - // return back the response to client - withdraw_request_db_object.receiver_flo_address = - params.trader_flo_address; - let - withdrawal_request_response = + ).then(withdraw_request_db_object=>{ + // return back the response to client + withdraw_request_db_object.receiver_flo_address = + params.trader_flo_address; + withdraw_request_db_object.trader_flo_address = + params.trader_flo_address; RM_RPC.send_rpc .call(this, "withdrawal_request_response", withdraw_request_db_object - ); - doSend( - withdrawal_request_response - ); + ).then(withdrawal_request_response=> + doSend(withdrawal_request_response)); + }); + return true; } catch (error) { console.log(error); @@ -12069,11 +12937,18 @@ break; case "retrieve_shamirs_secret_btc_pvtkey": - RM_RPC.filter_legit_requests(null, function (is_valid_request) { + RM_RPC.filter_legit_requests(params.trader_flo_address, function (is_valid_request) { if (is_valid_request !== true) return false; - if (typeof params.btc_private_key_array !== "string" || typeof params.retrieve_pvtkey_req_id !== - "string") return false; + if (typeof params.btc_private_key_array !== "string" + || typeof params.retrieve_pvtkey_req_id !== "string") return false; + + let rec_flo_id = params.receiver_flo_address || request.globalParams.receiverFloId; + if (typeof rec_flo_id == "undefined" || rec_flo_id !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + + if(typeof params.db_inst !== "string") throw new Error(`ERROR: No DB instance provided.`); + if(params.db_inst!==localbitcoinplusplus.wallets.my_local_flo_address) return; let btc_private_key_str = params.btc_private_key_array; let retrieve_pvtkey_req_id = params.retrieve_pvtkey_req_id; @@ -12103,11 +12978,7 @@ await RM_TRADE.resolve_current_crypto_price_in_fiat( withdraw_res.product, withdraw_res.currency); - const EqCryptoWd = RM_TRADE.calculateCryptoEquivalentOfCash( - withdraw_res.receiverBTCEquivalentInCash, - withdraw_res.currency, - withdraw_res.product); - EqCryptoWd = parseFloat(EqCryptoWd); + const EqCryptoWd = withdraw_res.receivingBTC; let transaction_key = btc_reserves.supernode_transaction_key; @@ -12116,120 +12987,79 @@ RM_WALLET.rebuild_private_key( btc_pk_shares_array, transaction_key); - //console.log(btc_private_key); + console.log(btc_private_key); RM_TRADE.sendTransaction( withdraw_res.product, withdraw_res.utxo_addr, btc_private_key, withdraw_res.receiverBTCAddress, - withdraw_res.receiverBTCEquivalentInCash, - withdraw_res.currency, + withdraw_res.receivingBTC, withdraw_res.change_adress, async function (res) { - console.log( - res - ); - if (typeof res == - "string" && - res.length > - 0) { + console.log(res); + if (typeof res == "object") { try { - let - resp_obj = - JSON - .parse( - res - ); - let - resp_txid = - resp_obj - .txid - .result || - resp_obj - .txid; - let - msg = - `Transaction Id for your withdrawn crypto asset: ${resp_txid}`; + let msg = ''; + if (typeof res.txid=="string" && res.txid.length>0) { + resp_obj = JSON.parse(res.txid); + resp_txid = resp_obj.txid.result || resp_obj.txid; + msg = `Transaction Id for your withdrawn crypto asset: ${resp_txid}.`; + } else if (res.signedTxHash.length>0) { + msg = `INFO: We could not broadcast your transaction. Please broadcast + this Signed Raw Tx manually yourself: ${res.signedTxHash}`; + } else { + console.log(res); + throw new Errror(`ERROR: Failed to make transaction.`); + return false; + } - readDB - ( - 'crypto_balances', - withdraw_res - .id - ) - .then( - res_bal => { - btc_eq_receiving_amount - = - parseFloat( - btc_eq_receiving_amount - ) - .toFixed( - 8 + if (msg.length>0) { + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "supernode_message", { + "trader_flo_address": withdraw_res.trader_flo_address, + "receiver_flo_address": withdraw_res.trader_flo_address, + "server_msg": msg + }).then(server_response=>doSend(server_response)); + } else return; + const withdrawer_crypto_bal_id = `${withdraw_res.trader_flo_address}_${withdraw_res.product}`; + readDB('crypto_balances', withdrawer_crypto_bal_id) + .then(res_bal => { + if (typeof res_bal !== "object") { + throw new Error(`FATAL ERROR: Failed to subtract balance of id ${withdrawer_crypto_bal_id} by ${EqCryptoWd}. `); + } + res_bal.crypto_balance -= EqCryptoWd; + updateinDB('crypto_balances', res_bal, withdrawer_crypto_bal_id) + .then(res_obj => { + const res_obj_str = JSON.stringify(res_obj); + const res_obj_hash = Crypto.SHA256(res_obj_str); + const res_obj_sign = RM_WALLET .sign( + res_obj_hash, + localbitcoinplusplus + .wallets + .MY_SUPERNODE_PRIVATE_KEY ); - res_bal - .crypto_balance -= - EqCryptoWd; - updateinDB - ( - 'crypto_balances', - res_bal, - withdraw_res - .id - ) - .then( - res_obj => { - const - res_obj_str = - JSON - .stringify( - res_obj - ); - const - res_obj_hash = - Crypto - .SHA256( - res_obj_str - ); - const - res_obj_sign = - RM_WALLET - .sign( - res_obj_hash, - localbitcoinplusplus - .wallets - .MY_SUPERNODE_PRIVATE_KEY - ); - const - updateUserCryptoBalanceObject = { - updatedBTCBalanceObject: res_bal, - updatedBTCBalanceObjectSign: res_obj_sign, - trader_flo_address: withdraw_res.trader_flo_address, - receiver_flo_address: withdraw_res.trader_flo_address, - } + const updateUserCryptoBalanceObject = { + updatedBTCBalanceObject: res_obj, + updatedBTCBalanceObjectSign: res_obj_sign, + trader_flo_address: withdraw_res.trader_flo_address, + receiver_flo_address: withdraw_res.trader_flo_address, + } - const - updateUserCryptoBalanceRequestObject = - RM_RPC - .send_rpc( - "updateUserCryptoBalanceRequest", - updateUserCryptoBalanceObject - ); - doSend - ( - updateUserCryptoBalanceRequestObject - ); - - } - ) + RM_RPC + .send_rpc( + "updateUserCryptoBalanceRequest", + updateUserCryptoBalanceObject + ).then(updateUserCryptoBalanceRequestObject=> + doSend(updateUserCryptoBalanceRequestObject)); + }); } ); - } catch ( - error - ) { + } catch (error) { console.warn(error); showMessage(error); } @@ -12241,60 +13071,125 @@ *******************CHECK ACTUAL BTC BALANCE HERE THROUGH AN API AND UPDATE DEPOSIT TABLE**************************************************** ************************************************************************************************************************************/ - readDBbyIndex - ( - 'deposit', - 'btc_address', - withdraw_res - .utxo_addr - ).then( - function ( - deposit_arr_resp - ) { - if ( - typeof deposit_arr_resp == - "object" - ) { - deposit_arr_resp - .map( - deposit_arr => { - deposit_arr - .bitcoinToBePaid -= - EqCryptoWd; + readDBbyIndex('deposit', 'btc_address', withdraw_res.utxo_addr) + .then(deposit_arr_resp => { + if (typeof deposit_arr_resp == "object") { + deposit_arr_resp.map(async deposit_arr => { - if ( - deposit_arr - .bitcoinToBePaid > - 0 - ) { - // update deposits in db - deposit_arr - .status = - 2; // UTXO ready to be used again - updateinDB - ( - "deposit", - deposit_arr, - deposit_arr - .trader_flo_address - ); - - } else { - // delete entry in deposits in db - removeinDB - ( - "deposit", - deposit_arr - .trader_flo_address - ); - } - } - ); - return true; + let explorer; + let decimal = 100000000; + switch (deposit_arr.product) { + case "BTC": + explorer = localbitcoinplusplus.server.btc_mainnet; + break; + case "BTC_TEST": + explorer = localbitcoinplusplus.server.btc_testnet; + break; + case "FLO": + explorer = localbitcoinplusplus.server.flo_mainnet; + decimal = 1; + break; + case "FLO_TEST": + explorer = localbitcoinplusplus.server.flo_testnet; + decimal = 1; + break; + default: + break; } - }); + if(typeof explorer !== "string") { + throw new Error(`WARNING: Invalid product value: ${deposit_arr.product}.`); + return false; + } + + const bal_url = `${explorer}/api/addr/${withdraw_res.utxo_addr}/balance`; + console.log(bal_url); + + let current_balance = await helper_functions.ajaxGet(bal_url); + if (!isNaN(current_balance) && parseFloat(current_balance) > 0) { + current_balance = Number(parseFloat(current_balance/decimal)); + } + + if (typeof current_balance=="number") { + deposit_arr.bitcoinToBePaid = current_balance; + btc_reserves.balance = current_balance; + } else { + deposit_arr.bitcoinToBePaid -= EqCryptoWd; + btc_reserves.balance -= EqCryptoWd; + } + + if (deposit_arr.bitcoinToBePaid > 0) { + // update deposits in db + deposit_arr.status = 2; // UTXO ready to be used again + const deposit_resp = await updateinDB("deposit", deposit_arr, deposit_arr.id) + // Update new balance in system_btc_reserves + const reserves_resp = await updateinDB("system_btc_reserves_private_keys", + btc_reserves, btc_reserves.id); + + // Send the resp to backups + RM_RPC + .send_rpc( + "update_deposited_crypto_instance", + { + deposit_data: deposit_resp, + btc_reserve_data: reserves_resp, + db_inst: params.db_inst, + trader_flo_address: deposit_arr.trader_flo_address + } + ).then(delRequestObject=> + doSend(delRequestObject)); + + // Do not delete these data instantly as the data + // may be required by a follow-up withdraw request + localbitcoinplusplus.actions.delay(900000).then(()=>{ + removeinDB('withdraw_btc', withdraw_id); + + RM_RPC + .send_rpc( + "delete_deposited_crypto_instance", + { + withdraw_btc_id: withdraw_id, + db_inst: params.db_inst, + trader_flo_address: deposit_arr.trader_flo_address + } + ).then(delRequestObject=> + doSend(delRequestObject)); + }); + + // AND DO THE SAME ABOVE 2 IN BACKUP RECEIVE RPC + + + + } else { + // Do not delete these data instantly as the data + // may be required by a follow-up withdraw request + localbitcoinplusplus.actions.delay(900000).then(()=>{ + // delete entry in deposits in db + removeinDB("deposit", deposit_arr.id); + removeinDB('system_btc_reserves_private_keys', retrieve_pvtkey_req_id); + removeinDB('withdraw_btc', withdraw_id); + + RM_RPC + .send_rpc( + "delete_deposited_crypto_instance", + { + deposit_id: deposit_arr.id, + btc_reserve_id: retrieve_pvtkey_req_id, + withdraw_btc_id: withdraw_id, + db_inst: params.db_inst, + trader_flo_address: deposit_arr.trader_flo_address + } + ).then(delRequestObject=> + doSend(delRequestObject)); + }); + + } + }); + return true; + } }); + + }); } } }); @@ -12319,7 +13214,7 @@ let res_data_obj = { trader_flo_address: params.data.trader_flo_address, trader_flo_pubKey: params.data.trader_flo_pubKey, - trader_status: params.data.trader_status, + trader_reputation: params.data.trader_reputation, timestamp: params.data.timestamp }; let res_data_hash = Crypto.SHA256(JSON.stringify(res_data_obj)); @@ -12367,6 +13262,1283 @@ return request.toString(); // return to client }, + async backup_receive_rpc_response(request) { + var request = JSON.parse(request); + var params = request.params[0]; + var method = request.method; + + if (typeof params == "object" && typeof method == "string") { + + if (typeof params.receiver_flo_address=="string" + && params.receiver_flo_address.length>0) { + if(params.receiver_flo_address + !== localbitcoinplusplus.wallets.my_local_flo_address) return; + } + + let recvr_flo_id = params.receiver_flo_address || request.globalParams.receiverFloId; + if (typeof recvr_flo_id == "string" + && recvr_flo_id.length > 0 + && recvr_flo_id !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + + const RM_WALLET = new localbitcoinplusplus.wallets; + const RM_TRADE = new localbitcoinplusplus.trade; + const RM_RPC = new localbitcoinplusplus.rpc; + + let respective_trader_id = ''; + if (typeof params.trader_flo_address == "string") respective_trader_id = params.trader_flo_address; + 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; + + 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 == + "SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB" && params.trader_flo_address.length > + 0) { + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", + "crypto_balances", "cash_balances", "userPublicData", + "buyOrders", "sellOrders" + ]; + + localbitcoinplusplus.actions.get_sharable_db_data(tableArray, primarySupernodeOfThisUser).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, "server_sync_response", su_db_data) + .then(server_sync_response=> + doSend(server_sync_response)); + } + }); + } + }); + return; + } + + let backup_server_db_instance; + if (method !== "retrieve_shamirs_secret_btc_pvtkey") { + if (typeof localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser] == "object") { + 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); + }; + + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, async function (is_valid_request) { + if (is_valid_request !== true) return false; + + try { + // 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) { + return (respective_trader_id == user.trader_flo_address + && user.status==1); + }); + if (canUserTrade.includes(true)) { + request.response = + `Trader id ${respective_trader_id} is not clear for trade currently. + You must finish your previous pending orders to qualify again to trade.`; + + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "supernode_message", { + "trader_flo_address": respective_trader_id, + "receiver_flo_address": respective_trader_id, + "server_msg": request.response + }).then(server_response=> + doSend(server_response)); + + showMessage(request.response); + throw new Error(request.response); + return false; + } + } + }); + + // Check if user id is in deposit or withdraw. If true prevent him from trading + await backup_server_db_instance.backup_readAllDB('withdraw_cash').then(function (res) { + if (typeof res == "object") { + let check_deposit_withdraw_id_array = res.filter(f => f.status === 2) + .map(m => { + if (m.trader_flo_address == respective_trader_id || m.deposit_withdraw_id_array == + respective_trader_id) { + let server_msg = + `Trader id ${respective_trader_id} is not clear for trade currently. + You must finish your previous pending deposit/withdraw action to qualify again to trade.`; + + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "supernode_message", { + "trader_flo_address": respective_trader_id, + "receiver_flo_address": respective_trader_id, + "server_msg": server_msg + }).then(server_response=> + doSend(server_response)); + showMessage(server_msg); + throw new Error( + "User has not finished previous pending actions." + ); + } + }); + } + }); + } catch (error) { + throw new Error(error); + } + }); + + } + + switch (method) { + case "trade_buy": + + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, async function (is_valid_request) { + if (is_valid_request !== true) return false; + await RM_TRADE.resolve_current_crypto_price_in_fiat(params.product, + params.currency); + const get_requester_primary_supernode = await localbitcoinplusplus.kademlia + .determineClosestSupernode(params.trader_flo_address); + const primarySupernodeForThisUser = get_requester_primary_supernode[0].data.id; + let trade_margin = await RM_TRADE.getAssetTradeAndWithdrawLimit( + params.trader_flo_address, params.product, params.currency, primarySupernodeForThisUser); + if (typeof trade_margin.remaining_crypto_credit == "number" && + typeof trade_margin.remaining_fiat_credit == "number") { + if (trade_margin.remaining_fiat_credit > 0 && trade_margin.remaining_fiat_credit >= + params.buy_price) { + request.response = RM_TRADE.trade_buy.call(this, + ...request.params, + function (supernode_signed_res) { + supernode_signed_res.receiver_flo_address = params.trader_flo_address; + if (typeof supernode_signed_res == "object") { + RM_RPC + .send_rpc + .call(this, + "trade_buy_request_response", + supernode_signed_res) + .then(buy_request_response=> + doSend(buy_request_response)); + + // Init trading + RM_TRADE.createTradePipes(params.currency, primarySupernodeForThisUser); + return true; + } + }, primarySupernodeForThisUser); + } else { + err_msg = `Trade Margin Check Failed: You can only trade upto ${params.currency} ${trade_margin.remaining_fiat_credit}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } else { + err_msg = "Invalid trade margin figures."; + showMessage(err_msg); + throw new Error(err_msg); + } + + }); + break; + case "trade_sell": + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, async function (is_valid_request) { + if (is_valid_request !== true) return false; + await RM_TRADE.resolve_current_crypto_price_in_fiat(params.product, + params.currency); + const get_requester_primary_supernode = await localbitcoinplusplus.kademlia + .determineClosestSupernode(params.trader_flo_address); + const primarySupernodeForThisUser = get_requester_primary_supernode[0].data.id; + let trade_margin = await RM_TRADE.getAssetTradeAndWithdrawLimit( + params.trader_flo_address, params.product, params.currency, primarySupernodeForThisUser); + if (typeof trade_margin.remaining_crypto_credit == "number" && + typeof trade_margin.remaining_fiat_credit == "number") { + let eqCrypto = RM_TRADE.calculateCryptoEquivalentOfCash(params.buy_price, params.currency, params.product); + if (trade_margin.remaining_crypto_credit > 0 && trade_margin.remaining_crypto_credit >= + eqCrypto) { + request.response = RM_TRADE.trade_sell.call( + this, ...request.params, + function (supernode_signed_res) { + if (typeof supernode_signed_res == "object") { + supernode_signed_res.receiver_flo_address = params.trader_flo_address; + RM_RPC + .send_rpc + .call(this, + "trade_sell_request_response", + supernode_signed_res) + .then(sell_request_response=> + doSend(sell_request_response)); + + // Init trading + RM_TRADE.createTradePipes(params.currency, primarySupernodeForThisUser); + return true; + } + }, primarySupernodeForThisUser + ); + } else { + err_msg = `WARNING: Trade Margin Check Failed: + You can only trade upto ${params.currency} ${trade_margin.remaining_fiat_credit}.`; + + showMessage(err_msg); + throw new Error(err_msg); + } + } else { + err_msg = "Invalid trade margin figures."; + showMessage(err_msg); + throw new Error(err_msg); + } + }); + break; + case "sync_with_supernode": + // already processed above + break; + case "deposit_asset_request": + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, async function (is_valid_request) { + + if (is_valid_request !== true) return false; + + // This code will only run for supernodes + if (typeof params.product !== "undefined" + && (localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product) + || localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.product)) + && typeof params.depositing_amount !== "undefined" + && localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency) + && typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' + && localbitcoinplusplus.master_configurations.validTradingAmount + .includes(parseFloat(params.depositing_amount)) + && typeof params.trader_flo_address == "string" + && params.trader_flo_address.length > 0 + ) { + const requester_public_req = await backup_server_db_instance + .backup_readDB('userPublicData', params.trader_flo_address); + if (typeof requester_public_req!=="object") return; + const requester_public_key = requester_public_req.trader_flo_pubKey; + + if (typeof requester_public_key == "undefined" || + requester_public_key == null) { + err_msg = 'Failed to get public key of the user.'; + showMessage(err_msg); + throw new Error(err_msg); + } + params.depositor_public_key = requester_public_key; + + await RM_TRADE.resolve_current_crypto_price_in_fiat( + params.product, params.currency); + + if (localbitcoinplusplus.master_configurations.tradableAsset1 + .includes(params.product)) { + + let generate_btc_keys_for_requester = RM_WALLET + .generateFloKeys(null, params.product); + + console.table(generate_btc_keys_for_requester); + + params.id = helper_functions.unique_id(); + params.status = 1; + params.btc_address = + generate_btc_keys_for_requester.address; + + params.bitcoinToBePaid = RM_TRADE.calculateCryptoEquivalentOfCash( + params.depositing_amount, params.currency, + params.product); + + let receivedTradeInfo = { ...params }; + + if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == + "undefined") { + err_msg = 'Failed to determine Super node signing key.'; + showMessage(err_msg); + throw new Error(err_msg); + } + + readDB("localbitcoinUser", "00-01").then( + async function (su_data) { + if (typeof su_data == "object" && + typeof su_data.myLocalFLOPublicKey == + "string" && + su_data.myLocalFLOPublicKey.length > + 0 && + localbitcoinplusplus.master_configurations + .supernodesPubKeys + .includes(su_data.myLocalFLOPublicKey) + ) { + + let receivedTradeInfoHash = + Crypto.SHA256(JSON.stringify(receivedTradeInfo)); + + receivedTradeInfo["depositDataHash"] = receivedTradeInfoHash; + receivedTradeInfo["order_validator_sign"] = + RM_WALLET.sign(receivedTradeInfoHash, + localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY + ); + receivedTradeInfo["order_validator_public_key"] = su_data.myLocalFLOPublicKey; + + try { + const this_btc_pvt_key = + generate_btc_keys_for_requester.privateKeyWIF; + const this_btc_tx_key = + Crypto.util.randomBytes(64); + const + this_btc_pvt_key_shamirs_secret = + RM_WALLET.createShamirsSecretShares(this_btc_pvt_key, 10, 5); + if (typeof this_btc_pvt_key_shamirs_secret == + "object" && + this_btc_pvt_key_shamirs_secret + .length > 0) { + + const deposit_res = await backup_server_db_instance.backup_addDB("deposit", receivedTradeInfo); + + // Send the address to the requester + let deposit_response_object = { + error: false, + method: "deposit_asset_request_response", + msg: `Please send ${params.product} ${params.bitcoinToBePaid} to the following addres: ${generate_btc_keys_for_requester.address}.`, + data: deposit_res + }; + deposit_response_object.receiver_flo_address = params.trader_flo_address; + deposit_response_object.trader_flo_address = params.trader_flo_address; + + RM_RPC + .send_rpc + .call(this, + "deposit_asset_request_response", + deposit_response_object + ).then(deposit_request_response=> + doSend(deposit_request_response)); + + let + this_btc_pvt_key_shamirs_secret_array = + this_btc_pvt_key_shamirs_secret + .map(chunks => { + let + chunk_ids = + Crypto.util + .bytesToHex(Crypto.util.randomBytes(64)); + let + chunk_array = { + "id": chunk_ids, + "privateKeyChunks": Crypto + .AES + .encrypt( + chunks, + this_btc_tx_key + ), + "trader_flo_address": params.trader_flo_address + }; + return chunk_array; + }); + + // Send chunks of private keys to other supernodes + this_btc_pvt_key_shamirs_secret_array + .map(shares => { + RM_RPC + .send_rpc + .call( + this, + "store_shamirs_secret_pvtkey_shares", + shares + ).then(store_pvtkey_req=> + doSend(store_pvtkey_req)); + + }); + + if (typeof localbitcoinplusplus + .wallets.my_local_flo_address == + "string" && + typeof localbitcoinplusplus + .wallets.my_local_flo_public_key == + "string" && + typeof localbitcoinplusplus + .master_configurations + .supernodesPubKeys == + "object" && + localbitcoinplusplus + .master_configurations + .supernodesPubKeys.includes( + localbitcoinplusplus + .wallets.my_local_flo_public_key + )) { + try { + let + this_btc_pvt_key_shamirs_secret__id_array = + this_btc_pvt_key_shamirs_secret_array + .map(i => i.id); + let + btc_private_key_shamirs_id = + this_btc_pvt_key_shamirs_secret__id_array; + let + supernode_transaction_key = + this_btc_tx_key; + const + system_btc_reserves_private_keys_object = { + id: helper_functions + .unique_id(), + product: params + .product, + btc_address: params + .btc_address, + balance: null, + trader_flo_address: params + .trader_flo_address, + btc_private_key_shamirs_id: btc_private_key_shamirs_id, + supernode_transaction_key: supernode_transaction_key + } + backup_server_db_instance.backup_addDB( + "system_btc_reserves_private_keys", + system_btc_reserves_private_keys_object + ).then(pk_res=>{ + // Send it to neighbor nodes to store as backup + reactor.dispatchEvent('store_backup_crypto_pk_object', pk_res); + }); + } catch (error) { + throw new Error( error); + } + } + return true; + } + } catch (error) { + throw new Error(error); + } + + // Send the address to the requester + // let deposit_response_object = { + // error: false, + // method: "deposit_asset_request_response", + // msg: `Please send the ${params.product} to ${generate_btc_keys_for_requester.address}.`, + // data: deposit_res, + // receiver_flo_address: params.trader_flo_address, + // trader_flo_address: params.trader_flo_address, + // }; + + // RM_RPC.send_rpc + // .call(this, + // "deposit_asset_request_response", + // deposit_response_object + // ).then(deposit_request_response=> + // doSend(deposit_request_response)); + return true; + } + }); + + return false; + + } else if (!localbitcoinplusplus.master_configurations + .tradableAsset1.includes(params.product)) { + params.id = helper_functions.unique_id(); + params.status = 1; + // IMPORTANT - If deposit is a fiat make sure product and currency are both same + params.currency = params.product; + let receivedTradeInfo = { ...params }; + + backup_server_db_instance.backup_readDB("localbitcoinUser", "00-01").then( + function (su_data) { + if (typeof su_data == "object" && + typeof su_data.myLocalFLOPublicKey == + "string" && + su_data.myLocalFLOPublicKey.length > + 0 && + localbitcoinplusplus.master_configurations + .supernodesPubKeys + .includes(su_data.myLocalFLOPublicKey) + ) { + let receivedTradeInfoHash = + Crypto.SHA256(JSON.stringify( + receivedTradeInfo)); + + receivedTradeInfo[ + "depositDataHash"] = + receivedTradeInfoHash; + receivedTradeInfo[ + "order_validator_sign"] = + RM_WALLET.sign( + receivedTradeInfoHash, + localbitcoinplusplus.wallets + .MY_SUPERNODE_PRIVATE_KEY + ); + receivedTradeInfo[ + "order_validator_public_key" + ] = su_data.myLocalFLOPublicKey; + + // YOU NEED TO DETERMINE A BANK ACCOUNT HERE IF NO ONE IS WITHDRAWING + try { + backup_server_db_instance.backup_addDB("deposit", + receivedTradeInfo); + backup_server_db_instance.backup_readDBbyIndex( + "withdraw_cash", "status", 1) + .then(async function (withdrawers_list) { + if (typeof withdrawers_list == + "object") { + if ( + withdrawers_list.length > 0) { + withdrawers_list.filter( + wd => wd.currency == + params.currency).map( + async function (withdrawer) { + if ( + withdrawer.withdraw_amount == + params.depositing_amount && + withdrawer.currency == + params.currency + ) { + withdrawer.status = 2; // A depositor has been asked to deposit money + withdrawer.depositor_found_at = + new Date(); + withdrawer.depositor_flo_id = receivedTradeInfo.trader_flo_address; + const withdraw_resp = await backup_server_db_instance + .backup_updateinDB ("withdraw_cash", withdrawer, withdrawer.trader_flo_address); + + receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account + const receivedTradeInfo_resp = await backup_server_db_instance.backup_updateinDB( + "deposit", + receivedTradeInfo, + receivedTradeInfo.trader_flo_address + ); + + let withdrawer_bank_account = withdrawer.receivinAddress; + + let deposit_response_object = { + error: false, + method: "deposit_asset_request_response", + msg: `Please send the money to following bank address: "${withdrawer_bank_account}"`, + data: receivedTradeInfo_resp, + withdrawer_data: withdraw_resp, + receiver_flo_address: receivedTradeInfo.trader_flo_address, + trader_flo_address: receivedTradeInfo.trader_flo_address, + }; + + RM_RPC.send_rpc.call( + this, + "deposit_asset_request_response", + deposit_response_object + ).then(deposit_request_response=> + doSend(deposit_request_response)); + return true; + } else { + err_msg = "Deposit request failed: We could not find a withdrawer."; + showMessage(err_msg); + throw new Error(err_msg); + } + } + ); + } else { + //No one is withdrawing so provide your bank details + + receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account + const receivedTradeInfo_resp = await updateinDB + ( + "deposit", + receivedTradeInfo, + receivedTradeInfo + .trader_flo_address + ); + + let deposit_response_object = { + error: false, + method: "deposit_asset_request_response", + msg: `Please send the money to following bank address: "System determined bank".`, + data: receivedTradeInfo_resp, + receiver_flo_address: receivedTradeInfo.trader_flo_address, + trader_flo_address: receivedTradeInfo.trader_flo_address, + }; + + RM_RPC + .send_rpc + .call( + this, + "deposit_asset_request_response", + deposit_response_object + ).then(deposit_request_response=> + doSend(deposit_request_response)); + return true; + } + } + }); + } catch (error) { + err_msg = "Deposit request failed: We could not find a withdrawer. Come again later."; + showMessage(err_msg); + throw new Error(err_msg); + } + + } + }); + } + + } else { + err_msg = "deposit asset request error"; + showMessage(err_msg); + throw new Error(err_msg); + } + }); + break; + case "withdraw_request_method": + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, async function (is_valid_request) { + + if (is_valid_request !== true) return false; + + if (typeof params.product !== "undefined" + && ((localbitcoinplusplus.master_configurations.tradableAsset1.includes(params.product) + || localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.product)) + && localbitcoinplusplus.master_configurations.tradableAsset2.includes(params.currency)) + && typeof params.withdrawing_amount !== "undefined" + && typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' + && typeof params.trader_flo_address == "string" + && params.trader_flo_address.length > 0 + && typeof params.receivinAddress == "string" + && params.receivinAddress.length > 0 + && typeof params.currency == "string" + ) { + const get_requester_primary_supernode = await localbitcoinplusplus.kademlia + .determineClosestSupernode(params.trader_flo_address); + const primarySupernodeForThisUser = get_requester_primary_supernode[0].data.id; + + await RM_TRADE.resolve_current_crypto_price_in_fiat(params.product, + params.currency); + let trade_margin = await RM_TRADE.getAssetTradeAndWithdrawLimit( + params.trader_flo_address, params.product, params.currency + ); + + if (localbitcoinplusplus.master_configurations.tradableAsset1.includes( + params.product)) { + if (trade_margin.remaining_crypto_credit < 0 + || params.withdrawing_amount <= 0 + || trade_margin.remaining_crypto_credit < params.withdrawing_amount) { + err_msg = `Insufficient crypto balance to withdraw. You can withdraw upto: ${params.product} ${trade_margin.remaining_crypto_credit}` + showMessage(err_msg); + throw new Error(err_msg); + } + } else { + if (trade_margin.remaining_fiat_credit < 0 + || params.withdrawing_amount <= 0 + || trade_margin.remaining_fiat_credit < params.withdrawing_amount) { + err_msg = `Insufficient fiat balance to withdraw. You can withdraw upto: ${params.currency} ${trade_margin.remaining_fiat_credit}`; + showMessage(err_msg); + throw new Error(err_msg); + } + } + + params.id = helper_functions.unique_id(); + params.status = 1; + if (localbitcoinplusplus.master_configurations.tradableAsset1 + .includes(params.product)) { + // Check how much cryptos the user can withdraw + const withdrawer_btc_id = `${params.trader_flo_address}_${params.product}`; + backup_server_db_instance.backup_readDB("crypto_balances", withdrawer_btc_id) + .then(function (btc_balance_res) { + if (typeof btc_balance_res == "object" + && typeof btc_balance_res.trader_flo_address == "string" + && btc_balance_res.crypto_balance > 0) { + let withdrawer_btc_balance = Number(btc_balance_res.crypto_balance).toFixed(8); + const eqBTC = Number(parseFloat(params.withdrawing_amount).toFixed(8)); + let withdrawer_new_btc_balance = withdrawer_btc_balance - eqBTC; + if (withdrawer_new_btc_balance >= 0 + && withdrawer_btc_balance > 0 + && eqBTC > 0 + && eqBTC <= withdrawer_btc_balance + ) { + + // Now details of Bitcoins can be sent to withdrawer + + /**************************************************************************** + ***********IMPORTANT: CHANGE RECEIVING ADDRESS TO BTC THAN FLO HERE********** + ***********AND DO SOMETHING ABOUT PRIVATE KEY BELOW************************** + ****************************************************************************/ + let sum_total_btc = 0; + let valid_utxo_list = []; + let receiverBTCAddress = params.receivinAddress.trim(); + + backup_server_db_instance.backup_readAllDB("deposit").then(function ( + deposit_list) { + if (typeof deposit_list == "object" + && deposit_list.length > 0 + ) { + deposit_list = deposit_list.filter(deposits => + deposits.status == 2 + && + localbitcoinplusplus + .master_configurations + .tradableAsset1 + .includes(deposits.product) + && + params.product == deposits.product + ); + for (const dl in deposit_list) { + if (deposit_list.hasOwnProperty(dl)) { + const deposit_dl = deposit_list[dl]; + sum_total_btc += parseFloat(deposit_dl.bitcoinToBePaid); + + if (eqBTC <= sum_total_btc ) { + valid_utxo_list.push(deposit_dl); + break; + } else { + valid_utxo_list.push(deposit_dl); + } + } + } + let valid_btc_list = + valid_utxo_list.map( + deposit_arr => { + // Deposited Bitcoin is under process + deposit_arr.status = 3; + backup_server_db_instance.backup_updateinDB( + "deposit", + deposit_arr, + deposit_arr.trader_flo_address + ); + + // save the address and id in a table + const withdraw_id = helper_functions.unique_id(); + const withdraw_btc_order_object = { + id: withdraw_id, + trader_flo_address: params.trader_flo_address, + utxo_addr: deposit_arr.btc_address, + receiverBTCAddress: params.receivinAddress, + receivingBTC: eqBTC, + currency: params.currency, + product: params.product, + change_adress: deposit_arr.btc_address, + timestamp: + new Date() + } + backup_server_db_instance + .backup_addDB('withdraw_btc', withdraw_btc_order_object); + return { + withdraw_id: withdraw_id, + deposited_btc_address: deposit_arr.btc_address + }; + }); + + // doSend btc_private_key_shamirs_id from system_btc_reserves_private_keys + valid_btc_list.map(vbl => { + readDBbyIndex + ( + 'system_btc_reserves_private_keys', + 'btc_address', + vbl.deposited_btc_address + ).then( + function (res) { + let retrieve_pvtkey_req_id = res[0].id; + res[0] + .btc_private_key_shamirs_id + .map( + bpks => { + RM_RPC + .send_rpc + .call( + this, + "send_back_shamirs_secret_btc_pvtkey", { + retrieve_pvtkey_req_id: retrieve_pvtkey_req_id, + chunk_val: bpks, + withdraw_id: vbl.withdraw_id, + db_inst: primarySupernodeForThisUser + } + ).then(retrieve_pvtkey_req=>doSend(retrieve_pvtkey_req)); + + } + ); + }); + }); + + } + }); + + } else { + err_msg = `Withdrawal request failed: You are trying to withdraw more Bitcoins than you have.`; + showMessage(err_msg); + throw new Error(err_msg); + + // Return error to the requester + return { + error: true, + method: "withdrawal_request_response", + data: "Withdrawal request failed: You are trying to withdraw more Bitcoins than you have." + }; + } + } else { + err_msg = + `Withdrawal request failed: You don't seem to have any Bitcoin balance in the system yet. + Please buy some Bitcoins to withdraw.`; + showMessage(err_msg); + + // Return error to the requester + return { + error: true, + method: "withdrawal_request_response", + data: `Withdrawal request failed: You don't seem to have any Bitcoin balance in the system yet. + Please buy some Bitcoins to withdraw.` + }; + } + }); + } else if (localbitcoinplusplus.master_configurations.tradableAsset2 + .includes(params.product)) { + + if (!localbitcoinplusplus.master_configurations.validTradingAmount + .includes(parseFloat(params.withdrawing_amount))) { + err_msg = `Withdrawal request failed: Please enter valid fiat amount.`; + showMessage(err_msg); + throw new Error(err_msg); + } + // Check if there's no already a withdraw cash order of this user + /*ONLY DELETE A WITHDRAW ORDER WHEN A DEPOSITOR HAS CONFIRMED DEPOSIT + AND RECEIVER HAS CONFIRMED WITHDRAW*/ + + // Check how much Cash user can withdraw + const trader_cash_id = `${params.trader_flo_address}_${params.currency}`; + backup_server_db_instance.backup_readDB("cash_balances", trader_cash_id) + .then(function (cash_balances_res) { + if (typeof cash_balances_res == "object" + && typeof cash_balances_res.trader_flo_address == "string" + && typeof cash_balances_res.cash_balance == "number" + && cash_balances_res.cash_balance > 0 + ) { + let withdrawer_cash_balance = parseFloat(cash_balances_res.cash_balance); + let withdrawing_cash_amount = parseFloat(params.withdrawing_amount); + let bank_details = params.receivinAddress.trim(); + + if (withdrawer_cash_balance > 0 && + withdrawing_cash_amount > 0 && + withdrawer_cash_balance >= withdrawing_cash_amount + ) { + // Add it to cash withdrawal table + let withdraw_request_db_object = { + id: helper_functions.unique_id(), + trader_flo_address: params.trader_flo_address, + withdraw_amount: withdrawing_cash_amount, + currency: params.currency, + receivinAddress: bank_details, + status: 1, // withdraw request called + } + + const getPubKeyOfSupernodeOfThisUser = RM_WALLET + .getSupernodePublicKeyFromFloId(primarySupernodeForThisUser); + + if (typeof getPubKeyOfSupernodeOfThisUser == "string" + && getPubKeyOfSupernodeOfThisUser.length > 0 + && localbitcoinplusplus.master_configurations + .supernodesPubKeys.includes( + getPubKeyOfSupernodeOfThisUser + )) { + + let withdraw_request_db_object_hash = + Crypto.SHA256(JSON.stringify( + withdraw_request_db_object + )); + withdraw_request_db_object + ["withdrawDataHash"] = + withdraw_request_db_object_hash; + withdraw_request_db_object + ["order_validator_sign"] = + RM_WALLET + .sign( + withdraw_request_db_object_hash, + localbitcoinplusplus + .wallets.MY_SUPERNODE_PRIVATE_KEY + ); + withdraw_request_db_object + ["order_validator_public_key"] = + getPubKeyOfSupernodeOfThisUser; + + try { + // add the request to supernode db + backup_server_db_instance.backup_addDB( + "withdraw_cash", + withdraw_request_db_object + ).then(withdraw_request_db_object=>{ + // return back the response to client + withdraw_request_db_object.receiver_flo_address = + params.trader_flo_address; + withdraw_request_db_object.trader_flo_address = + params.trader_flo_address; + RM_RPC.send_rpc + .call(this, + "withdrawal_request_response", + withdraw_request_db_object + ).then(withdrawal_request_response=> + doSend(withdrawal_request_response)); + }); + + return true; + } catch (error) { + console.log(error); + } + } + + } else { + // Return error to the requester + err_msg = "Withdrawal request failed: You are trying to withdraw more cash than you have in localbitcoinplusplus account."; + showMessage(err_msg); + throw new Error(err_msg); + } + } + }); + } else { + err_msg = "withdraw request error"; + showMessage(err_msg); + throw new Error(err_msg); + } + } + }); + break; + + case "retrieve_shamirs_secret_btc_pvtkey": + RM_RPC.filter_legit_backup_requests(params.trader_flo_address, function (is_valid_request) { + if (is_valid_request !== true) return false; + + if (typeof params.btc_private_key_array !== "string" + || typeof params.retrieve_pvtkey_req_id !== "string") return false; + + let rec_flo_id = params.receiver_flo_address || request.globalParams.receiverFloId; + if (typeof rec_flo_id == "undefined" || rec_flo_id !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + + if(typeof params.db_inst !== "string") throw new Error(`ERROR: No DB instance provided.`); + if(params.db_inst==localbitcoinplusplus.wallets.my_local_flo_address) return; + + backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[params.db_inst]; + + if (typeof backup_server_db_instance !== "object") return; + + let btc_private_key_str = params.btc_private_key_array; + let retrieve_pvtkey_req_id = params.retrieve_pvtkey_req_id; + let withdraw_id = params.withdraw_id; + + try { + let btc_private_key_object = JSON.parse(btc_private_key_str); + let btc_pk_shares_array = btc_private_key_object.map(pkChunks => { + if (typeof pkChunks.private_key_chunk !== "undefined") + return pkChunks.private_key_chunk.privateKeyChunks; + }).filter(val => val !== undefined); + console.log(btc_pk_shares_array); + + backup_server_db_instance.backup_readDB('withdraw_btc', withdraw_id).then(function (withdraw_res) { + if (typeof withdraw_res == "object") { + backup_server_db_instance.backup_readDB('system_btc_reserves_private_keys', + retrieve_pvtkey_req_id).then(async function ( + btc_reserves) { + if (typeof btc_reserves == "object") { + + // Ideally this line should never run. + if (btc_reserves.product !== + withdraw_res.product) throw new Error( + "Mismatch of assets in withdrawal request." + ); + + await RM_TRADE.resolve_current_crypto_price_in_fiat( + withdraw_res.product, + withdraw_res.currency); + let EqCryptoWd = Number(withdraw_res.receivingBTC); + + let transaction_key = + btc_reserves.supernode_transaction_key; + if (transaction_key.length > 0) { + let btc_private_key = RM_WALLET.rebuild_private_key( + btc_pk_shares_array, transaction_key); + console.log(btc_pk_shares_array); + console.log(transaction_key); + + RM_TRADE.sendTransaction( + withdraw_res.product, + withdraw_res.utxo_addr, + btc_private_key, + withdraw_res.receiverBTCAddress, + withdraw_res.receivingBTC, + withdraw_res.change_adress, + async function (res) { + console.log(res); + if (typeof res == "object") { + try { + let msg = ''; + if (typeof res.txid=="string" && res.txid.length>0) { + resp_obj = JSON.parse(res.txid); + resp_txid = resp_obj.txid.result || resp_obj.txid; + msg = `Transaction Id for your withdrawn crypto asset: ${resp_txid}.`; + } else if (res.signedTxHash.length>0) { + msg = `INFO: We could not broadcast your transaction. Please broadcast + this Signed Raw Tx manually yourself: ${res.signedTxHash}`; + } else { + console.log(res); + throw new Errror(`ERROR: Failed to make transaction.`); + return false; + } + + if (msg.length>0) { + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "supernode_message", { + "trader_flo_address": withdraw_res.trader_flo_address, + "receiver_flo_address": withdraw_res.trader_flo_address, + "server_msg": msg + }).then(server_response=>doSend(server_response)); + } else return; + + let withdrawer_crypto_bal_id = `${withdraw_res.trader_flo_address}_${withdraw_res.product}`; + backup_server_db_instance.backup_readDB + ( + 'crypto_balances', + withdrawer_crypto_bal_id + ) + .then( + res_bal => { + if (typeof res_bal !== "object") { + throw new Error(`FATAL ERROR: Failed to subtract balance of id ${withdrawer_crypto_bal_id} by ${EqCryptoWd}. `); + } + res_bal.crypto_balance -= EqCryptoWd; + backup_server_db_instance.backup_updateinDB + ('crypto_balances', + res_bal, + withdrawer_crypto_bal_id + ).then(res_obj => { + const res_obj_str = JSON.stringify(res_obj); + const res_obj_hash = Crypto.SHA256(res_obj_str); + const res_obj_sign = RM_WALLET + .sign(res_obj_hash, + localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY); + + const updateUserCryptoBalanceObject = { + updatedBTCBalanceObject: res_obj, + updatedBTCBalanceObjectSign: res_obj_sign, + trader_flo_address: withdraw_res.trader_flo_address, + receiver_flo_address: withdraw_res.trader_flo_address, + } + + RM_RPC + .send_rpc( + "updateUserCryptoBalanceRequest", + updateUserCryptoBalanceObject + ).then(updateUserCryptoBalanceRequestObject=> + doSend(updateUserCryptoBalanceRequestObject)); + } + ) + + } + ); + } catch ( + error + ) { + console.warn(error); + showMessage(error); + } + } + + // Check if there's BTC left in deposited BTC. If yes update its status to 2 else delete it + + /*********************************************************************************************************************************** + *******************CHECK ACTUAL BTC BALANCE HERE THROUGH AN API AND UPDATE DEPOSIT TABLE**************************************************** + ************************************************************************************************************************************/ + + backup_server_db_instance.backup_readDBbyIndex + ( + 'deposit', + 'btc_address', + withdraw_res + .utxo_addr + ).then( + function ( + deposit_arr_resp + ) { + if ( + typeof deposit_arr_resp == + "object" + ) { + deposit_arr_resp + .map(async deposit_arr => { + let explorer; + let decimal = 100000000; + switch (deposit_arr.product) { + case "BTC": + explorer = localbitcoinplusplus.server.btc_mainnet; + break; + case "BTC_TEST": + explorer = localbitcoinplusplus.server.btc_testnet; + break; + case "FLO": + explorer = localbitcoinplusplus.server.flo_mainnet; + decimal = 1; + break; + case "FLO_TEST": + explorer = localbitcoinplusplus.server.flo_testnet; + decimal = 1; + break; + default: + break; + } + + if(typeof explorer !== "string") { + throw new Error(`WARNING: Invalid product value: ${deposit_arr.product}.`); + return false; + } + + const bal_url = `${explorer}/api/addr/${withdraw_res.utxo_addr}/balance`; + console.log(bal_url); + + let current_balance = await helper_functions.ajaxGet(bal_url); + if (!isNaN(current_balance) && parseFloat(current_balance) > 0) { + current_balance = Number(parseFloat(current_balance/decimal)); + } + + if (typeof current_balance=="number") { + deposit_arr.bitcoinToBePaid = current_balance; + } else { + deposit_arr.bitcoinToBePaid -= EqCryptoWd; + } + + if (deposit_arr.bitcoinToBePaid > 0) { + // update deposits in db + deposit_arr.status = 2; // UTXO ready to be used again + const deposit_resp = await backup_server_db_instance.backup_updateinDB + ( + "deposit", + deposit_arr, + deposit_arr.id + ); + + // Update new balance in system_btc_reserves + const reserves_resp = await backup_server_db_instance + .backup_updateinDB("system_btc_reserves_private_keys", + btc_reserves, btc_reserves.id); + + // Send the resp to backups + RM_RPC + .send_rpc( + "update_deposited_crypto_instance", + { + deposit_data: deposit_resp, + btc_reserve_data: reserves_resp, + db_inst: params.db_inst, + trader_flo_address: deposit_arr.trader_flo_address + } + ).then(delRequestObject=> + doSend(delRequestObject)); + + localbitcoinplusplus.actions.delay(900000).then(()=>{ + backup_server_db_instance.backup_removeinDB('withdraw_btc', withdraw_id); + + RM_RPC + .send_rpc( + "delete_deposited_crypto_instance", + { + withdraw_btc_id: withdraw_id, + db_inst: params.db_inst, + trader_flo_address: deposit_arr.trader_flo_address + } + ).then(delRequestObject=> + doSend(delRequestObject)); + }); + + } else { + + localbitcoinplusplus.actions.delay(900000).then(()=>{ + // delete entry in deposits in db + backup_server_db_instance.backup_removeinDB + ( + "deposit", + deposit_arr.id + ); + + backup_server_db_instance + .backup_removeinDB('system_btc_reserves_private_keys', + retrieve_pvtkey_req_id); + + backup_server_db_instance.backup_removeinDB('withdraw_btc', withdraw_id); + + RM_RPC + .send_rpc( + "delete_deposited_crypto_instance", + { + deposit_id: deposit_arr.id, + btc_reserve_id: retrieve_pvtkey_req_id, + withdraw_btc_id: withdraw_id, + db_inst: params.db_inst, + trader_flo_address: deposit_arr.trader_flo_address + } + ).then(delRequestObject=> + doSend(delRequestObject)); + }); + + } + } + ); + return true; + } + }); + + }); + } + } + }); + } + }); + + } catch (error) { + throw new Error(error); + } + + }); + break; + + case "superNodeSignedAddUserPublicData": + if (typeof params == "object" && typeof params.data == "object") { + RM_RPC.filter_legit_backup_requests(params.data.trader_flo_address, + function (is_valid_request) { + if (is_valid_request !== true) return false; + + if (typeof params.su_pubKey == "string" && localbitcoinplusplus + .master_configurations.supernodesPubKeys.includes(params.su_pubKey)) { + let res_data_obj = { + trader_flo_address: params.data.trader_flo_address, + trader_flo_pubKey: params.data.trader_flo_pubKey, + trader_reputation: params.data.trader_reputation, + timestamp: params.data.timestamp + }; + let res_data_hash = Crypto.SHA256(JSON.stringify(res_data_obj)); + let res_data_verification = RM_WALLET + .verify(res_data_hash, params.sign, params.su_pubKey); + if ((res_data_verification == true) && res_data_hash == params.data_hash) { + backup_server_db_instance.backup_addDB('userPublicData', params.data); + return true; + } + } + }); + } + break; + + case "update_external_file_server_response": + if (typeof params == "object") { + if (params.filename == "UPDATE_ALL_FILES") { + let file_details_str = JSON.stringify(params.file_updated); + if (RM_WALLET.verify(file_details_str, + params.server_sign, params.server_pubkey)) { + params.file_updated.map(new_file => { + backup_server_db_instance.backup_updateinDB("external_files", new_file); + createScript(new_file.filename, new_file.content); + }); + return true; + } + } else { + let file_details_string = JSON.stringify(params.file_updated); + if (RM_WALLET.verify(file_details_string, + params.server_sign, params.server_pubkey)) { + backup_server_db_instance.backup_updateinDB("external_files", params.file_updated); + createScript(params.file_updated.filename, params.file_updated.content); + return true; + } + } + showMessage(`WARNING: Failed to update external files from server.`); + } + break; + + default: + break; + } + } + return request.toString(); // return to client + }, + parse_server_rpc_response(request) { var request = JSON_RPC.parse(request); var response; @@ -12466,7 +14638,7 @@ let placeNewOrder = RM_RPC.send_rpc.call(this, this .rpc_job, newOrderDataObj); return placeNewOrder; - + } else if (typeof is_valid_order == "object") { var err; for (err = 0; err < is_valid_order.length; err++) { @@ -12475,8 +14647,10 @@ return false; } }, - trade_buy(params, callback) { + trade_buy(params, callback, backup_db="") { let err_msg; + let _addDB = addDB; + let _readDB = readDB; for (var key in params) { if (params.hasOwnProperty(key)) { if (typeof key == undefined || key.trim() == "" || key == null) { @@ -12495,13 +14669,25 @@ throw new Error(err_msg); } + 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]; + _addDB = foreign_db.backup_addDB.bind(foreign_db); + _readDB = foreign_db.backup_readDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } + const RM_WALLET = new localbitcoinplusplus.wallets; const RM_TRADE = new localbitcoinplusplus.trade; const RM_RPC = new localbitcoinplusplus.rpc; //Check buyer's fiat balance const trader_cash_id = `${params.trader_flo_address}_${params.currency}`; - readDB("cash_balances", trader_cash_id).then(function (res) { + _readDB("cash_balances", trader_cash_id).then(function (res) { if (typeof res !== "undefined" && typeof res.cash_balance == "number" && !isNaN(res.cash_balance)) { let buyer_cash_balance = parseFloat(res.cash_balance); let buy_price_btc = parseFloat(params.buy_price); @@ -12520,11 +14706,10 @@ // supernode data query readDB('localbitcoinUser', '00-01').then(function (user_data) { - if (typeof user_data == "object" && typeof localbitcoinplusplus.wallets - .MY_SUPERNODE_PRIVATE_KEY == - "string" && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY - .length > - 0) { + if (typeof user_data == "object" + && typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == "string" + && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length > 0 + ) { try { // Add buy oder params['id'] = helper_functions.unique_id(); @@ -12539,10 +14724,9 @@ params["supernodePubKey"] = user_data.myLocalFLOPublicKey; params["status"] = 1; - addDB("buyOrders", params); + _addDB("buyOrders", params) + .then(resp=>callback(resp)); - // Send data for further action - callback(params); } catch (error) { console.error(error); callback(false); @@ -12562,8 +14746,10 @@ } }); }, - trade_sell(params, callback) { + trade_sell(params, callback, backup_db="") { let err_msg; + let _addDB = addDB; + let _readDB = readDB; for (var key in params) { if (params.hasOwnProperty(key)) { if (typeof key == "undefined" || key.trim() == "" || key == null) { @@ -12582,13 +14768,25 @@ throw new Error(err_msg); } + 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]; + _addDB = foreign_db.backup_addDB.bind(foreign_db); + _readDB = foreign_db.backup_readDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } + const RM_WALLET = new localbitcoinplusplus.wallets; const RM_TRADE = new localbitcoinplusplus.trade; const RM_RPC = new localbitcoinplusplus.rpc; // Check crypto balance of the seller let seller_btc_id = `${params.trader_flo_address}_${params.product}`; - readDB("crypto_balances", seller_btc_id).then(function (res) { + _readDB("crypto_balances", seller_btc_id).then(function (res) { if (typeof res !== "undefined" && typeof res.trader_flo_address == "string" && res.trader_flo_address .length > 0 && res.crypto_balance > 0) { let seller_btc_balance = parseFloat(res.crypto_balance); @@ -12624,9 +14822,8 @@ params["supernodePubKey"] = user_data.myLocalFLOPublicKey; params["status"] = 1; - addDB("sellOrders", params); - - callback(params); + _addDB("sellOrders", params) + .then(resp=>callback(resp)); } }); callback(false); @@ -12671,9 +14868,9 @@ receiver_flo_address:localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } const RM_RPC = new localbitcoinplusplus.rpc; - let deposit_request = RM_RPC.send_rpc.call(this, - "deposit_asset_request", deposit_request_object); - doSend(deposit_request); + RM_RPC.send_rpc.call(this, + "deposit_asset_request", deposit_request_object) + .then(deposit_request=>doSend(deposit_request)); }, withdrawAsset(assetType, amount, receivinAddress, userFLOaddress, currency, callback) { let err_msg; @@ -12712,9 +14909,10 @@ receiver_flo_address:localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } const RM_RPC = new localbitcoinplusplus.rpc; - let withdraw_request = RM_RPC.send_rpc.call(this, - "withdraw_request_method", withdraw_request_object); - doSend(withdraw_request); + RM_RPC.send_rpc.call(this, + "withdraw_request_method", withdraw_request_object) + .then(withdraw_request=>doSend(withdraw_request)) + }, calculateCryptoEquivalentOfCash(price, currency = "USD", crypto_code) { if (localbitcoinplusplus.master_configurations.validTradingAmount.includes(price)) { @@ -12723,50 +14921,215 @@ const RM_TRADE = new localbitcoinplusplus.trade; let current_crypto_price = RM_TRADE.get_current_crypto_price_in_fiat(crypto_code, currency); if (typeof current_crypto_price == "object" && current_crypto_price.rate > 0) { - return parseFloat(price / current_crypto_price.rate).toFixed(8); + return Number(parseFloat(price / current_crypto_price.rate).toFixed(8)); } } - let err_msg = `Failed to calculate crypto equivalent of cash.`; + let err_msg = `WARNING: Failed to calculate crypto equivalent of cash.`; showMessage(err_msg); throw new Error(err_msg); }, get_current_crypto_price_in_fiat(crypto_code, currency_code) { - return localbitcoinplusplus.trade[`current_${crypto_code}_price_in_${currency_code}`]; + let current_rate_obj = localbitcoinplusplus.trade[`current_${crypto_code}_price_in_${currency_code}`]; + if (typeof current_rate_obj=="object" + && localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(current_rate_obj.supernode_pub_key)) { + if (crypto_code=="BTC_TEST") { + crypto_code = "BTC" + } + if (crypto_code=="FLO_TEST") { + crypto_code = "FLO" + } + let rate_obj = { + id: `${crypto_code}_${currency_code}`, + crypto_code: crypto_code, + currency_code: currency_code, + rate: current_rate_obj.rate, + timestamp: current_rate_obj.timestamp + }; + const rate_obj_str = JSON.stringify(rate_obj); + const rate_obj_hash = Crypto.SHA256(rate_obj_str); + + const RM_WALLET = new localbitcoinplusplus.wallets; + if (RM_WALLET.verify(rate_obj_hash, current_rate_obj.sign, current_rate_obj.supernode_pub_key)) { + return current_rate_obj; + } + return false; + } }, async resolve_current_crypto_price_in_fiat(crypto_code, currency_code) { - let today = +new Date(); + let today = + new Date(); const RM_TRADE = new localbitcoinplusplus.trade; - let last_update_of_fiat_price_obj = RM_TRADE.get_current_crypto_price_in_fiat(crypto_code, - currency_code); + let last_update_of_fiat_price_obj = RM_TRADE.get_current_crypto_price_in_fiat(crypto_code, currency_code); if (typeof last_update_of_fiat_price_obj !== "object" || (today - last_update_of_fiat_price_obj.timestamp > 3600000)) { - last_update_of_fiat_price_obj = await RM_TRADE.set_current_crypto_price_in_fiat(crypto_code, - currency_code); + last_update_of_fiat_price_obj = await RM_TRADE.set_current_crypto_price_in_fiat(crypto_code, currency_code); return last_update_of_fiat_price_obj; } else { return last_update_of_fiat_price_obj; } }, + usd_to_fiat_exchange_rate(currency_code='INR') { + return new Promise((resolve, reject)=>{ + helper_functions.ajaxGet('http://apilayer.net/api/live?access_key=3abc51aa522420e4e185ac22733b0f30') + .then(res=>{ + if (typeof res=="object" && typeof res.quotes.USDINR=="number") { + + Object.defineProperty(localbitcoinplusplus.trade, + `current_USD_price_in_${currency_code}`, { + value: { + rate: res.quotes.USDINR, + timestamp: +new Date() + }, + writable: true, + configurable: false, + enumerable: true + }); + + localbitcoinplusplus.trade[`current_USD_price_in_${currency_code}`]; + + resolve(res['quotes'][`USD${currency_code}`]); + return; + } + reject(false); + }) + }); + }, + // This function should be run periodically maybe through cron job + update_fiat_to_crypto_exchange_rate(crypto_code="", fiat="") { + this.fiat_to_crypto_exchange_rate_from_API(crypto_code, fiat) + .then(new_price=>{ + console.log(new_price); + if (typeof new_price=="number") { + + let rate_obj = { + id: `${crypto_code}_${fiat}`, + crypto_code: crypto_code, + currency_code: fiat, + rate: new_price, + timestamp: +new Date() + }; + const rate_obj_str = JSON.stringify(rate_obj); + const rate_obj_hash = Crypto.SHA256(rate_obj_str); + const RM_WALLET = new localbitcoinplusplus.wallets; + const rate_obj_sign = RM_WALLET.sign(rate_obj_hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY); + rate_obj['supernode_pub_key'] = localbitcoinplusplus.wallets.my_local_flo_public_key; + rate_obj['sign'] = rate_obj_sign; + + updateinDB('crypto_fiat_rates', rate_obj) + .then(()=>{ + showMessage(`INFO: ${crypto_code}<=>${fiat} rate updated.`); + this.resolve_current_crypto_price_in_fiat(crypto_code, fiat); + }) + .catch(()=>console.error(`ERROR: Failed to update ${crypto_code}<=>${fiat} rate.`)); + + return true; + } else { + console.error(`ERROR: Failed to get valid response while fetching ${crypto_code}<=>${fiat} rate.`); + } + }).catch(e=>{ + console.error(e); + }); + }, + fiat_to_crypto_exchange_rate_from_API(crypto_code="", fiat="") { + return new Promise((resolve, reject)=>{ + if (crypto_code=="BTC") { + + helper_functions.ajaxGet(`https://api.coindesk.com/v1/bpi/currentprice.json`) + .then(async res=>{ + if (typeof res=="object" && typeof res.bpi.USD.rate_float=="number") { + let new_price = res.bpi.USD.rate_float; + if (fiat=="INR") { + let usd_to_fiat_price = await this.usd_to_fiat_exchange_rate(fiat); + new_price = Number(new_price*usd_to_fiat_price); + } + resolve(new_price); + return; + } else { + reject(false); + } + }).catch(e=>{ + console.error(e); + reject(false); + }); + } else if(crypto_code=="FLO") { + helper_functions.ajaxGet('https://min-api.cryptocompare.com/data/histoday?fsym=FLO&tsym=USD&limit=1&aggregate=3&e=CCCAGG') + .then(async res=>{ + if(typeof res== 'object' && typeof res.Data[0].close=="number") { + let new_price = res.Data[0].close; + if (fiat=="INR") { + let usd_to_fiat_price = await this.usd_to_fiat_exchange_rate(fiat); + new_price = Number(new_price*usd_to_fiat_price); + } + resolve(new_price); + return; + } + else { + let new_price = 0.08; + if (fiat=="INR") { + let usd_to_fiat_price = await this.usd_to_fiat_exchange_rate(fiat); + new_price = Number(new_price*usd_to_fiat_price); + } + resolve(new_price); + return; + } + reject(false); + }); + } + }); + }, + fiat_to_crypto_exchange_rate(crypto_code="", fiat="") { + return new Promise((resolve, reject)=>{ + try { + let id = `${crypto_code}_${fiat}`; + readDB('crypto_fiat_rates', id) + .then(res=>{ + if (typeof res=="object") { + return resolve(res); + } + reject(false); + }); + } catch(e) { + reject(e); + } + }); + }, async set_current_crypto_price_in_fiat(crypto_code, currency_code) { - if (!localbitcoinplusplus.master_configurations.tradableAsset1.includes(crypto_code) || - !localbitcoinplusplus.master_configurations.tradableAsset2.includes(currency_code)) return false; - let new_price = 1000000; + if (!localbitcoinplusplus.master_configurations.tradableAsset1.includes(crypto_code) + || !localbitcoinplusplus.master_configurations.tradableAsset2.includes(currency_code) + || !localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return false; - /************************** - Fetch latest rates here - ***************************/ + let rate_obj = null; - // if(crypto_code=="BTC" || crypto_code=="BTC_TEST") { - // new_price = (currency_code=="USD") ? 3540 : 300000; - // } else if(crypto_code=="FLO" || crypto_code=="FLO_TEST") { - // new_price = (currency_code=="USD") ? 0.08 : 5.8; - // } + if (crypto_code=="BTC" && currency_code=="USD") { + rate_obj = await this.fiat_to_crypto_exchange_rate(crypto_code, currency_code); + } + if (crypto_code=="BTC_TEST" && currency_code=="USD") { + rate_obj = await this.fiat_to_crypto_exchange_rate("BTC", currency_code); + } + if (crypto_code=="BTC" && currency_code=="INR") { + rate_obj = await this.fiat_to_crypto_exchange_rate(crypto_code, currency_code); + } + if (crypto_code=="BTC_TEST" && currency_code=="INR") { + rate_obj = await this.fiat_to_crypto_exchange_rate("BTC", currency_code); + } + if (crypto_code=="FLO" && currency_code=="USD") { + rate_obj = await this.fiat_to_crypto_exchange_rate(crypto_code, currency_code); + } + if (crypto_code=="FLO_TEST" && currency_code=="USD") { + rate_obj = await this.fiat_to_crypto_exchange_rate("FLO", currency_code); + } + if (crypto_code=="FLO" && currency_code=="INR") { + rate_obj = await this.fiat_to_crypto_exchange_rate(crypto_code, currency_code); + } + if (crypto_code=="FLO_TEST" && currency_code=="INR") { + rate_obj = await this.fiat_to_crypto_exchange_rate("FLO", currency_code); + } + + if(rate_obj == null) throw new Error(`WARNING: Failed to get price.`); + Object.defineProperty(localbitcoinplusplus.trade, `current_${crypto_code}_price_in_${currency_code}`, { - value: { - rate: new_price, - timestamp: +new Date() - }, + value: rate_obj, writable: true, configurable: false, enumerable: true @@ -12774,19 +15137,27 @@ return localbitcoinplusplus.trade[`current_${crypto_code}_price_in_${currency_code}`]; }, sendTransaction(crypto_type, utxo_addr, utxo_addr_wif, receiver_address, receiving_amount, - receiving_amount_currency = null, change_adress, callback) { + change_adress, callback) { let blockchain_explorer; - let divDecimal = 1; + let miners_fee = 0.0003; + const miner_fee_obj=JSON.parse(localbitcoinplusplus.master_configurations.miners_fee); if (crypto_type == "BTC") { blockchain_explorer = localbitcoinplusplus.server.btc_mainnet; + miners_fee = miner_fee_obj.btc; } else if (crypto_type == "BTC_TEST") { blockchain_explorer = localbitcoinplusplus.server.btc_testnet; + miners_fee = miner_fee_obj.btc; } else if (crypto_type == "FLO") { blockchain_explorer = localbitcoinplusplus.server.flo_mainnet; - divDecimal = 100000000; + miners_fee = miner_fee_obj.flo; } else if (crypto_type == "FLO_TEST") { blockchain_explorer = localbitcoinplusplus.server.flo_testnet; - divDecimal = 100000000; + miners_fee = miner_fee_obj.flo; + } + + if(typeof blockchain_explorer !== "string") { + showMessage(`WARNING: Please select cryptocurrency/fiat value from select bar.`); + return false; } let url = `${blockchain_explorer}/api/addr/${utxo_addr}/utxo`; @@ -12797,28 +15168,16 @@ if (utxo_list.length > 0) { try { - let btc_eq_receiving_amount = receiving_amount; - - if (typeof receiving_amount_currency == "string") { - if (!localbitcoinplusplus.master_configurations.validTradingAmount.includes( - receiving_amount)) { - err_msg = `ERROR: Amount value is invalid.`; - showMessage(err_msg); - throw new Error(err_msg); - } - const RM_TRADE = new localbitcoinplusplus.trade; - btc_eq_receiving_amount = RM_TRADE.calculateCryptoEquivalentOfCash( - receiving_amount, receiving_amount_currency, crypto_type); - btc_eq_receiving_amount = parseFloat(btc_eq_receiving_amount).toFixed(8); - } + let btc_eq_receiving_amount = Number(receiving_amount); + btc_eq_receiving_amount = Number(btc_eq_receiving_amount.toFixed(8)); let trx = bitjs[crypto_type].transaction(); let sum = 0; for (var key in utxo_list) { - if (utxo_list[key].confirmations > 0) { + if (utxo_list[key].confirmations !== 0) { var obj = utxo_list[key]; - sum += obj.satoshis / divDecimal; + sum += obj.amount; if (btc_eq_receiving_amount <= sum) { trx.addinput(obj.txid, obj.vout, obj.scriptPubKey); @@ -12829,14 +15188,29 @@ } } - //sum = parseFloat(sum / 100000000).toFixed(8); + if (sum <=0) { + console.log(utxo_list); + throw new Error('ERROR: No amount found in UTXO.'); + } - let change_amount = sum - btc_eq_receiving_amount - 0.00006060; + // Output cannot be greater than input + if (sum < btc_eq_receiving_amount) { + btc_eq_receiving_amount = sum; + } + btc_eq_receiving_amount = btc_eq_receiving_amount - miners_fee; trx.addoutput(receiver_address, btc_eq_receiving_amount); - trx.addoutput(change_adress, change_amount); + + let change_amount = 0; + if (sum - btc_eq_receiving_amount - miners_fee > 0) { + change_amount = sum - btc_eq_receiving_amount - miners_fee; + } + + if (change_amount>0) { + trx.addoutput(change_adress, change_amount); + } var sendFloData = - `localbitcoinpluslus tx: Send ${btc_eq_receiving_amount} satoshis to ${receiver_address}.`; //flochange adding place for flodata -- need a validation of 1024 chars + `localbitcoinpluslus tx: Send ${btc_eq_receiving_amount} ${crypto_type} to ${receiver_address}.`; //flochange adding place for flodata -- need a validation of 1024 chars if (crypto_type == "FLO" || crypto_type == "FLO_TEST") { trx.addflodata(sendFloData); // flochange .. create this function } @@ -12846,18 +15220,39 @@ let signedTxHash = trx.sign(utxo_addr_wif, 1); //SIGHASH_ALL DEFAULT 1 showMessage(`Signed Transaction Hash: ${signedTxHash}`); - + console.log(signedTxHash); + var http = new XMLHttpRequest(); var tx_send_url = `${blockchain_explorer}/api/tx/send`; var params = `{"rawtx":"${signedTxHash}"}`; http.open('POST', tx_send_url, true); http.setRequestHeader('Content-type', 'application/json'); http.onreadystatechange = function () { //Call a function when the state changes. - if (http.readyState == 4 && http.status == 200) { - showMessage(http.responseText); - callback(http.responseText); + if (http.readyState == 4) { + if (http.status == 200) { + console.log(http.responseText); + let response_obj = { + signedTxHash: signedTxHash, + txid: http.responseText + } + //callback(http.responseText); + callback(response_obj); + } else { + let response_obj = { + signedTxHash: signedTxHash, + txid: "" + } + callback(response_obj); + } } } + http.onerror = function() { + let response_obj = { + signedTxHash: signedTxHash, + txid: "" + } + callback(response_obj); + } http.send(params); } catch (error) { @@ -12869,16 +15264,27 @@ throw new Error(error); } } - }).catch(e => console.error(`No balance found in ${utxo_addr}: ${e}`)); + }).catch(e => console.error(`ERROR: Failed to send tx from utxo ${utxo_addr}: ${e}`)); }, /*Finds the best buy sell id match for a trade*/ - createTradePipes(trading_currency = "USD") { + createTradePipes(trading_currency = "USD", backup_db="") { + let _readAllDB = readAllDB; + 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); + } + } try { - readAllDB("sellOrders").then(function (sellOrdersList) { + _readAllDB("sellOrders").then(function (sellOrdersList) { if (sellOrdersList.length > 0) { sellOrdersList = sellOrdersList.filter(sellOrder => sellOrder.currency == trading_currency); - readAllDB("buyOrders").then(function (buyOrdersList) { + _readAllDB("buyOrders").then(function (buyOrdersList) { if (buyOrdersList.length > 0) { buyOrdersList = buyOrdersList.filter(buyOrder => buyOrder.currency == trading_currency); @@ -12908,8 +15314,7 @@ let n = buyPipe.value.length < sellPipe.value.length ? buyPipe.value.length : sellPipe.value.length; - if (buyPipe.value.length > 0 && sellPipe.value.length > - 0) { + if (buyPipe.value.length > 0 && sellPipe.value.length > 0) { const RM_TRADE = new localbitcoinplusplus.trade; const RM_RPC = new localbitcoinplusplus.rpc; for (let i = 0; i < n; i++) { @@ -12918,14 +15323,13 @@ function (supernode_res) { if (typeof supernode_res == "object") { - let server_res = - RM_RPC.send_rpc.call( + RM_RPC.send_rpc.call( this, "trade_balance_updates", - supernode_res); - doSend(server_res); + supernode_res) + .then(server_res=>doSend(server_res)) } - }); + }, backup_db); } } }); @@ -12937,7 +15341,31 @@ console.error(e); } }, - launchTrade(buyPipeObj, sellPipeObj, callback) { + launchTrade(buyPipeObj, sellPipeObj, callback, backup_db="") { + let _addDB = addDB; + let _readDB = readDB; + let _readDBbyIndex = readDBbyIndex; + let _readAllDB = readAllDB; + let _updateinDB = updateinDB; + let _removeinDB = removeinDB; + let _removeByIndex = removeByIndex; + let _removeAllinDB = removeAllinDB; + 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]; + _addDB = foreign_db.backup_addDB.bind(foreign_db); + _readDB = foreign_db.backup_readDB.bind(foreign_db); + _readDBbyIndex = foreign_db.backup_readDBbyIndex.bind(foreign_db); + _readAllDB = foreign_db.backup_readAllDB.bind(foreign_db); + _updateinDB = foreign_db.backup_updateinDB.bind(foreign_db); + _removeinDB = foreign_db.backup_removeinDB.bind(foreign_db); + _removeByIndex = foreign_db.backup_removeByIndex.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } if (buyPipeObj.order_type == "buy" && sellPipeObj.order_type == "sell" && buyPipeObj.buy_price == sellPipeObj.buy_price && buyPipeObj.currency == sellPipeObj.currency && @@ -12948,7 +15376,7 @@ let err_msg; // Check buyer's cash balance const buyer_cash_id = `${buyPipeObj.trader_flo_address}_${buyPipeObj.currency}`; - readDB("cash_balances", buyer_cash_id).then(function (buyPipeCashRes) { + _readDB("cash_balances", buyer_cash_id).then(function (buyPipeCashRes) { if (typeof buyPipeCashRes == "object" && typeof buyPipeCashRes.cash_balance == "number") { let buyer_cash_balance = parseFloat(buyPipeCashRes.cash_balance); @@ -12968,11 +15396,11 @@ // Check seller's crypto balance let seller_btc_id = `${sellPipeObj.trader_flo_address}_${sellPipeObj.product}`; - readDB("crypto_balances", seller_btc_id).then(function (sellPipeBTCRes) { + _readDB("crypto_balances", seller_btc_id).then(function (sellPipeBTCRes) { if (typeof sellPipeBTCRes == "object" && typeof sellPipeBTCRes.crypto_balance == "number") { - let seller_btc_balance = parseFloat(sellPipeBTCRes.crypto_balance) - .toFixed(8); + let seller_btc_balance = Number(parseFloat(sellPipeBTCRes.crypto_balance) + .toFixed(8)); let sell_price_in_inr = parseFloat(sellPipeObj.buy_price); let eqBTCSeller = RM_TRADE.calculateCryptoEquivalentOfCash( sell_price_in_inr, buyPipeObj.currency, buyPipeObj.product @@ -12990,7 +15418,7 @@ let buyerBTCResponseObject; let buyer_btc_id = `${buyPipeObj.trader_flo_address}_${buyPipeObj.product}`; - readDB("crypto_balances", buyPipeObj.trader_flo_address).then( + _readDB("crypto_balances", buyer_btc_id).then( function (buyPipeBTCRes) { if (typeof buyPipeBTCRes == "object" && typeof buyPipeBTCRes .crypto_balance == "number") { @@ -13001,7 +15429,7 @@ } else { // The user bought BTC for first time buyerBTCResponseObject = { - id: `${buyPipeObj.trader_flo_address}_${buyPipeObj.product}`, + id: buyer_btc_id, trader_flo_address: buyPipeObj.trader_flo_address, crypto_balance: eqBTCBuyer, crypto_currency: buyPipeObj.product @@ -13023,7 +15451,7 @@ let sellerCashResponseObject; const seller_cash_id = `${sellPipeObj.trader_flo_address}_${buyPipeObj.currency}`; - readDB("cash_balances", seller_cash_id).then( + _readDB("cash_balances", seller_cash_id).then( function (sellPipeCashRes) { if (typeof sellPipeCashRes == "object" && typeof sellPipeCashRes @@ -13052,7 +15480,7 @@ let new_seller_btc_balance = seller_btc_balance - eqBTCSeller; - new_seller_btc_balance = parseFloat(new_seller_btc_balance).toFixed(8); + new_seller_btc_balance = Number(parseFloat(new_seller_btc_balance).toFixed(8)); let sellerBTCResponseObject = { id: `${sellPipeObj.trader_flo_address}_${sellPipeObj.product}`, trader_flo_address: sellPipeObj @@ -13075,14 +15503,8 @@ .length > 0) { // Delete orders try { - removeinDB( - "buyOrders", - buyPipeObj - .id); - removeinDB( - "sellOrders", - sellPipeObj - .id); + _removeinDB("buyOrders", buyPipeObj.id); + _removeinDB("sellOrders", sellPipeObj.id); } catch (error) { callback(false); showMessage(`WARNING: Failed to delete respective buy and sell orders in an operation.`); @@ -13091,25 +15513,25 @@ // Update balances try { - updateinDB( + _updateinDB( "cash_balances", buyerCashResponseObject, buyPipeObj .trader_flo_address ); - updateinDB( + _updateinDB( "cash_balances", sellerCashResponseObject, sellPipeObj .trader_flo_address ); - updateinDB( + _updateinDB( "crypto_balances", buyerBTCResponseObject, buyPipeObj .trader_flo_address ); - updateinDB( + _updateinDB( "crypto_balances", sellerBTCResponseObject, sellPipeObj @@ -13174,20 +15596,23 @@ .wallets.MY_SUPERNODE_PRIVATE_KEY ); - let - response_for_client = { - "trade_infos": trade_infos, - "buyer_cash_data": buyerCashResponseObject, - "seller_cash_data": sellerCashResponseObject, - "buyer_btc_data": buyerBTCResponseObject, - "seller_btc_data": sellerBTCResponseObject, - "data_hash": hashed_data, - "supernode_sign": signed_data, - "supernodePubKey": user_data.myLocalFLOPublicKey, - "receiver_flo_address": "BROADCAST_TO_NODES", - } - callback(response_for_client); - return true; + localbitcoinplusplus.kademlia.determineClosestSupernode(buyPipeObj.trader_flo_address) + .then(getPrimarySuObj=>{ + let response_for_client = { + "trade_infos": trade_infos, + "buyer_cash_data": buyerCashResponseObject, + "seller_cash_data": sellerCashResponseObject, + "buyer_btc_data": buyerBTCResponseObject, + "seller_btc_data": sellerBTCResponseObject, + "data_hash": hashed_data, + "supernode_sign": signed_data, + "supernodePubKey": user_data.myLocalFLOPublicKey, + "trader_flo_address": getPrimarySuObj[0].data.id, + } + callback(response_for_client); + return true; + }); + } }); }); @@ -13218,26 +15643,40 @@ receiver_flo_address:localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } - let cancel_trade_request = RM_RPC.send_rpc.call(this, - "cancel_trade", cancel_trade_request_object); - doSend(cancel_trade_request); + RM_RPC.send_rpc.call(this, + "cancel_trade", cancel_trade_request_object) + .then(cancel_trade_request=>doSend(cancel_trade_request)); + }, - getAssetTradeAndWithdrawLimit(flo_id, crypto, fiat) { + getAssetTradeAndWithdrawLimit(flo_id, crypto, fiat, backup_db="") { + let _readDB = readDB; + let _readDBbyIndex = readDBbyIndex; + 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]; + _readDB = foreign_db.backup_readDB.bind(foreign_db); + _readDBbyIndex = foreign_db.backup_readDBbyIndex.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } const RM_TRADE = new localbitcoinplusplus.trade; let user_crypto_id = `${flo_id}_${crypto}`; let user_fiat_id = `${flo_id}_${fiat}`; - const user_balance_crypto_promise = readDB("crypto_balances", user_crypto_id); - const user_balance_fiat_promise = readDB("cash_balances", user_fiat_id); - const user_sell_orders_promise = readDBbyIndex("sellOrders", "trader_flo_address", flo_id) + const user_balance_crypto_promise = _readDB("crypto_balances", user_crypto_id); + const user_balance_fiat_promise = _readDB("cash_balances", user_fiat_id); + const user_sell_orders_promise = _readDBbyIndex("sellOrders", "trader_flo_address", flo_id) .then((res) => res.filter(resp => resp.currency == fiat && resp.product == crypto)); - const user_buy_orders_promise = readDBbyIndex("buyOrders", "trader_flo_address", flo_id) + const user_buy_orders_promise = _readDBbyIndex("buyOrders", "trader_flo_address", flo_id) .then((res) => res.filter(resp => resp.currency == fiat && resp.product == crypto)); - const user_fiat_withdraw_request_promise = readDBbyIndex("withdraw_cash", "trader_flo_address", + const user_fiat_withdraw_request_promise = _readDBbyIndex("withdraw_cash", "trader_flo_address", flo_id); - const user_crypto_withdraw_request_promise = readDBbyIndex("withdraw_btc", "trader_flo_address", + const user_crypto_withdraw_request_promise = _readDBbyIndex("withdraw_btc", "trader_flo_address", flo_id); return Promise.all([user_balance_crypto_promise, user_balance_fiat_promise, @@ -13262,14 +15701,13 @@ let sell_order_crypto_equivalent = 0; user_sell_orders.map(sell_orders => { sell_order_crypto_eq = RM_TRADE.calculateCryptoEquivalentOfCash( - sell_orders.sell_price, sell_orders.currency, sell_orders.product); + sell_orders.buy_price, sell_orders.currency, sell_orders.product); sell_order_crypto_equivalent += Number(sell_order_crypto_eq); }); let withdraw_crypto_equivalent = 0; user_crypto_withdraw_request.map(req => { - withdraw_crypto_eq = RM_TRADE.calculateCryptoEquivalentOfCash(req.receiverBTCEquivalentInCash, - req.currency, req.product); + withdraw_crypto_eq = req.receivingBTC; withdraw_crypto_equivalent += Number(withdraw_crypto_eq); }); @@ -13294,6 +15732,7 @@ }).catch(e => console.warn(e)); }, + } @@ -13310,8 +15749,17 @@ //proceed only when the second promise is resolved return data; } catch (error) { - showMessage(`WARNING: Failed to get data from ${url}.`); - throw new Error(error); + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.onload = function() { + if (xhr.status === 200) { + callback(xhr.responseText); + } else { + showMessage(`WARNING: Failed to get data from ${url}.`); + throw new Error(`Request to ${url} failed: ${xhr.status}`); + } + }; + xhr.send(); } }, @@ -13549,7 +15997,10 @@ newKeys.address.length > 0) { localbitcoinplusplusObj.myLocalFLOAddress = newKeys.address; localbitcoinplusplusObj.myLocalFLOPublicKey = newKeys.pubKeyHex; + localbitcoinplusplusObj.lastConnectedTime = + new Date(); + await updateinDB("localbitcoinUser", localbitcoinplusplusObj); + // launch KBuckets launchKBuckects = await localbitcoinplusplus.kademlia.launchKBucket(newKeys.address); @@ -13559,7 +16010,7 @@ throw new Error(kmsg); } - updateinDB("localbitcoinUser", localbitcoinplusplusObj, "00-01"); + await localbitcoinplusplus.kademlia.restoreSupernodeKBucket(); wsUri = await localbitcoinplusplus.kademlia.getSupernodeSeed( localbitcoinplusplusObj.myLocalFLOAddress); @@ -13571,39 +16022,52 @@ showMessage(`INFO: Added Supernode Id ${d.trader_flo_address} to KBucket.`); }); - localbitcoinplusplus.kademlia.restoreSupernodeKBucket(); - await startWebSocket(`ws://${wsUri[0].ip}:${wsUri[0].port}`); RM_WALLET.distributeShamirsSecretShares(newKeys.privateKeyWIF) .then(() => privateKeyBuilder()) .finally(()=>{ - if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' - && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length<1) { - setTimeout(function(){ + setTimeout(function(){ + if ((typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY!=='string' + || localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length<1) + ) { RM_WALLET.manually_assign_my_private_key(); + loadExternalFiles(); + dataBaseUIOperations(); + } else if(typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' && + localbitcoinplusplus.is_ui_loaded == false) { loadExternalFiles(); dataBaseUIOperations(); - clearTimeout(); - }, 10000); - } + } + clearTimeout(); + }, 10000); }); // Connect with backup supernodes - wsUri.filter((uri, index)=>{ - if(index>0 && localbitcoinplusplus.master_configurations.supernodesPubKeys - .includes(localbitcoinplusplusObj.myLocalFLOPublicKey)) { - return uri; - } - }).map((uri, index)=>{ - console.log(uri); - backUpSupernodesWS[uri.trader_flo_address] = new backupSupernodesWebSocketObject(`ws://${uri.ip}:${uri.port}`); - backUpSupernodesWS[uri.trader_flo_address].connectWS(); + if(localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(idbData.myLocalFLOPublicKey)) { - let dbname = `su_backup_${uri.trader_flo_address}`; - BACKUP_DB[uri.trader_flo_address] = new newBackupDB(dbname); - BACKUP_DB[uri.trader_flo_address].createNewDB(); - }); + // Invoke functions + reactor.dispatchEvent('user_flo_keys_active', idbData); + + wsUri.map((uri, index)=>{ + + // Do not serve to any requests unless data is fully synced. + localbitcoinplusplus.services[`can_serve_${uri.trader_flo_address}`] = false; + + if (index>0) { + let dbname = `su_backup_${uri.trader_flo_address}`; + BACKUP_DB[uri.trader_flo_address] = new newBackupDB(dbname); + BACKUP_DB[uri.trader_flo_address].createNewDB(); + } + }); + + // Connect to nearest live backup nodes + reactor.dispatchEvent('resolve_backup_ws_connections'); + + } + + localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS = wsUri[0].trader_flo_address; resolve(true); return; @@ -13617,17 +16081,20 @@ // launch KBucekts launchKBuckects = await localbitcoinplusplus.kademlia.launchKBucket(idbData.myLocalFLOAddress); - + if (!launchKBuckects) { const kmsg = `ERROR: Failed to build KBuckets. System cannot proceed further.`; showMessage(kmsg); throw new Error(kmsg); } + await localbitcoinplusplus.kademlia.restoreSupernodeKBucket(); + wsUri = await localbitcoinplusplus.kademlia.getSupernodeSeed(idbData.myLocalFLOAddress); if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(idbData.myLocalFLOPublicKey)) { - if (wsUri[0].trader_flo_address !== idbData.myLocalFLOAddress) { + const getClosestSuList = await readAllDB('myClosestSupernodes'); + if (wsUri[0].trader_flo_address !== idbData.myLocalFLOAddress || getClosestSuList.length < localbitcoinplusplus.master_configurations.MaxBackups) { showMessage(`INFO: Invalid connection. Refreshing the closest supernode list in DB.`); wsUri = await localbitcoinplusplus.kademlia.updateClosestSupernodeSeeds(idbData.myLocalFLOAddress); } @@ -13640,41 +16107,53 @@ showMessage(`INFO: Added Supernode Id ${d.trader_flo_address} to KBucket.`); }); - localbitcoinplusplus.kademlia.restoreSupernodeKBucket(); - // Connect with primary supernodes await startWebSocket(`ws://${wsUri[0].ip}:${wsUri[0].port}`); // rebuild private key await privateKeyBuilder(); - setTimeout(function(){ - if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY!=='string' - || localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length<1) { + setTimeout(function() { + if ((typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY!=='string' + || localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length<1) + ) { RM_WALLET.manually_assign_my_private_key(); + loadExternalFiles(); + dataBaseUIOperations(); + } else if(typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' && + localbitcoinplusplus.is_ui_loaded == false) { loadExternalFiles(); dataBaseUIOperations(); - clearTimeout(); } }, 10000); localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS = wsUri[0].trader_flo_address; + localbitcoinplusplus.services = {}; + // Connect with backup supernodes - wsUri.filter((uri, index)=>{ - if(index>0 && localbitcoinplusplus.master_configurations.supernodesPubKeys + if (localbitcoinplusplus.master_configurations.supernodesPubKeys .includes(idbData.myLocalFLOPublicKey)) { - return uri; - } - }).map((uri, index)=>{ - console.log(uri); - backUpSupernodesWS[uri.trader_flo_address] = new backupSupernodesWebSocketObject(`ws://${uri.ip}:${uri.port}`); - backUpSupernodesWS[uri.trader_flo_address].connectWS(); - let dbname = `su_backup_${uri.trader_flo_address}`; - BACKUP_DB[uri.trader_flo_address] = new newBackupDB(dbname); - BACKUP_DB[uri.trader_flo_address].createNewDB(); - }); + // Invoke functions + reactor.dispatchEvent('user_flo_keys_active', idbData); + + wsUri.map((uri, index)=>{ + + // Do not serve to any requests unless data is fully synced. + localbitcoinplusplus.services[`can_serve_${uri.trader_flo_address}`] = false; + + if (index>0) { + let dbname = `su_backup_${uri.trader_flo_address}`; + BACKUP_DB[uri.trader_flo_address] = new newBackupDB(dbname); + BACKUP_DB[uri.trader_flo_address].createNewDB(); + } + }); + + // Connect to nearest live backup nodes + reactor.dispatchEvent('resolve_backup_ws_connections'); + + } resolve(true); }); @@ -13687,444 +16166,142 @@ this.ws_connection = null; } backupSupernodesWebSocketObject.prototype = { - - async handle_backup_server_messages(evt) { - var response = evt.data; - console.log('backup response: '+response); - - let isItANodeLeavingMessage = response.search(`\\-- left`); - if(isItANodeLeavingMessage >= 0) { - reactor.dispatchEvent('fireNodeGoodByeEvent', response); - } - - var res_pos = response.indexOf('{'); - if (res_pos >= 0) { - let isRequestToLinkIp = response.search("linkMyLocalIPToMyFloId"); - let incoming_msg_local_ip = ``; - if (isRequestToLinkIp>=0) { - let index_of_ip = response.indexOf(' '); - if (incoming_msg_local_ip>=0) { - incoming_msg_local_ip = response.substr(0, index_of_ip); - } - } - var res = response.substr(res_pos); - try { - - const isIncomingMessageValid = await validateIncomingMessage(res); - console.log("isIncomingMessageValid (Backup): ", isIncomingMessageValid); - - var res_obj = JSON.parse(res); - - if (typeof res_obj.method !== "undefined") { - let response_from_sever; - - const RM_WALLET = new localbitcoinplusplus.wallets; - const RM_TRADE = new localbitcoinplusplus.trade; - const RM_RPC = new localbitcoinplusplus.rpc; - - switch (res_obj.method) { - case "give_me_your_backup": - if (typeof res_obj.params == "object" - && typeof res_obj.params[0] == "object") { - let received_resp = res_obj.params[0]; - RM_RPC.filter_legit_backup_requests(received_resp.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request || received_resp.JOB!=="BACKUP_SERVER_REQUEST") return; - const requester_supernode_pubkey = received_resp.requesters_pub_key; - const requester_supernode_flo_address = received_resp.trader_flo_address; - - const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", - "crypto_balances", "cash_balances", "userPublicData" - ]; - localbitcoinplusplus.actions.get_sharable_db_data(tableArray).then( - function (su_db_data) { - if (typeof su_db_data == "object") { - su_db_data.trader_flo_address = localbitcoinplusplus.wallets.my_local_flo_address; - su_db_data.receiver_flo_address = requester_supernode_flo_address; - let server_sync_response = RM_RPC - .send_rpc - .call(this, "backup_server_sync_response", - su_db_data); - doSend(server_sync_response); - } - }); - - }); - } - break; - - case "server_sync_response": - if (typeof res_obj.params == "object" - && typeof res_obj.params[0] == "object") { - let su_backup_db_data = res_obj.params[0]; - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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); - }; - - (async function () { - for (let tableStoreName in su_backup_db_data) { - // skip loop if the property is from prototype - if (tableStoreName == 'trader_flo_address' || !su_backup_db_data.hasOwnProperty( - tableStoreName)) continue; - - try { - let obj = su_backup_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 backup_server_db_instance.backup_updateinDB(tableStoreName, - obj[prop], obj[prop].trader_flo_address); - } - } - } else { - let resdbdata = await backup_server_db_instance.backup_removeAllinDB(tableStoreName); - if (resdbdata !== false) { - if (obj.length > 0) { - for (var prop in obj) { - if (!obj.hasOwnProperty(prop)) continue; - await backup_server_db_instance.backup_addDB(resdbdata, obj[prop]); - } - } - } - } - - } catch (error) { - console.log(error); - } - } - })(); - - }); - } - break; - - case "trade_buy_request_response": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - - if(!is_valid_request) return false; - - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - let buyOrders_data = res_obj.params[0]; - - if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && - localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( - buyOrders_data.supernodePubKey)) { - let isDataSignedBySuperNode = RM_WALLET - .verify(buyOrders_data.data_hash, buyOrders_data.supernode_sign, - buyOrders_data.supernodePubKey); - if (isDataSignedBySuperNode === true) { - - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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); - }; - - // Add buy order - backup_server_db_instance.backup_addDB("buyOrders", buyOrders_data).then(() => { - showMessage(`Your buy order is placed successfully.`); - }); - } - } - } - }); - break; - case "trade_sell_request_response": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - let sellOrders_data = res_obj.params[0]; - if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && - localbitcoinplusplus.master_configurations.supernodesPubKeys - .includes(sellOrders_data.supernodePubKey)) { - let isDataSignedBySuperNode = RM_WALLET - .verify(sellOrders_data.data_hash, sellOrders_data.supernode_sign, - sellOrders_data.supernodePubKey); - if (isDataSignedBySuperNode === true) { - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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); - }; - - // Add buy order - backup_server_db_instance.backup_addDB("sellOrders", sellOrders_data).then(() => { - showMessage(`Your sell order is placed successfully.`); - });; - } - } - } - }); - break; - - case "deposit_asset_request_response": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" - && typeof res_obj.params[0].data == "object") { - let resp = res_obj.params[0]; - if (RM_WALLET - .verify(resp.data.depositDataHash, resp.data.order_validator_sign, resp.data.order_validator_public_key) - ) { - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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_addDB('deposit', resp.data); - if (typeof resp.withdrawer_data == "object") { - backup_server_db_instance.backup_updateinDB("withdraw_cash", resp.withdrawer_data, resp.withdrawer_data.trader_flo_address); - } - backup_server_db_instance.backup_readDB("localbitcoinUser", "00-01").then(function (user) { - if (typeof user == "object" && user.myLocalFLOAddress == resp.data.trader_flo_address) { - let counterTraderAccountAddress = - `Please pay the amount to following address:
-${resp.msg}
`; - showMessage(counterTraderAccountAddress); - modalWindow(counterTraderAccountAddress); - } - }); - } - } - }); - break; - - case "withdrawal_request_response": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - if (RM_WALLET - .verify(res_obj.params[0].withdrawDataHash, res_obj.params[0].order_validator_sign, - res_obj.params[0].order_validator_public_key)) { - - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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_addDB('withdraw_cash', res_obj.params[0]).then(() => { - showMessage(`Your cash withdrawal request is placed successfully.`); - }); - } - } - }); - break; - - case "cancel_trade": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - let cancel_request = res_obj.params[0]; - if (cancel_request.job == "cancel_trade_request") { - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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("userPublicData", cancel_request.trader_flo_address).then((trader_data) => { - if (typeof trader_data.trader_flo_address !== "string" || typeof trader_data - .trader_flo_pubKey !== "string") { - err_msg="ERROR: Failed to cancel the trade. User is unknown."; - showMessage(err_msg); - throw new Error(err_msg); - } - tradeDB = cancel_request.trade_type == "buy" ? "buyOrders" : - "sellOrders"; - if (RM_WALLET - .verify(cancel_request.trade_id, cancel_request.signed_trade_id, - trader_data.trader_flo_pubKey)) { - backup_server_db_instance.backup_removeinDB(tradeDB, cancel_request.trade_id) - .then((id) => showMessage(`Trade Id ${id} deleted.`)); - } else { - showMessage( - `Failed to verify trade for trade id ${cancel_request.trade_id}` - ); - } - }) - } else { - showMessage("Failed to cancel trade."); - } - } - }); - break; - - case "update_all_withdraw_cash_depositor_claim": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - let depositor_claim_response_object = res_obj.params[0]; - let update_withdraw_cash_obj_data_res = { - depositor_claim: depositor_claim_response_object.depositor_claim - }; - let update_withdraw_cash_obj_data_res_str = JSON.stringify( - update_withdraw_cash_obj_data_res); - let depositor_claim_response_data_hash = Crypto.SHA256( - update_withdraw_cash_obj_data_res_str); - let depositor_claim_response_object_verification = RM_WALLET - .verify(depositor_claim_response_data_hash, depositor_claim_response_object.sign, - depositor_claim_response_object.publicKey); - - if ((depositor_claim_response_data_hash == depositor_claim_response_object.hash) && - (depositor_claim_response_object_verification == true)) { - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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_updateinDB('withdraw_cash', depositor_claim_response_object.depositor_claim, - depositor_claim_response_object.depositor_claim.id); - return true; - } - return false; - } - }); - break; - - case "update_all_deposit_withdraw_success": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - let withdraw_success_response = res_obj.params[0]; - let update_cash_balance_obj_res = { - depositor_cash_data: withdraw_success_response.depositor_cash_data - } - let update_cash_balance_obj_res_str = JSON.stringify(update_cash_balance_obj_res); - let update_cash_balance_obj_res_hash = Crypto.SHA256( - update_cash_balance_obj_res_str); - let update_cash_balance_obj_res_verification = RM_WALLET - .verify(update_cash_balance_obj_res_hash, withdraw_success_response.sign, - withdraw_success_response.publicKey); - - if ((update_cash_balance_obj_res_hash == withdraw_success_response.hash) && - update_cash_balance_obj_res_verification == true) { - const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[res_obj.globalParams.senderFloId]; - - 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_updateinDB('cash_balances', withdraw_success_response.depositor_cash_data); - backup_server_db_instance.backup_updateinDB('cash_balances', withdraw_success_response.withdrawer_cash_data); - backup_server_db_instance.backup_removeByIndex('deposit', 'trader_flo_address', withdraw_success_response.depositor_cash_data - .trader_flo_address); - backup_server_db_instance.backup_removeinDB('withdraw_cash', withdraw_success_response.withdraw_id); - return true; - } - return false; - } - }); - break; - case "requestSupernodesToRemoveAUserFloIdFromTheirKBucket": - RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, - function (is_valid_request) { - if(!is_valid_request) return false; - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - const removeKBReq = res_obj.params[0]; - const currentSupernodeFloIdOfUser = removeKBReq.currentSupernodeFloId; - if(localbitcoinplusplus.wallets.my_local_flo_address == currentSupernodeFloIdOfUser) return; - const userKbucketObject_id_array = Object.values(removeKBReq.redundantKbucketNodeU8Id); - const userKBId = new Uint8Array(userKbucketObject_id_array); - KBucket.remove(userKBId); - return true; - } - }); - break; - case "link_My_Local_IP_To_My_Flo_Id": - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - const req_params = res_obj.params[0]; - if(typeof req_params.requesters_pub_key !== "string") return; - let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); - if(typeof flo_addr_for_pubkey !== "string") return; - if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; - updateinDB('ipTable', { - 'flo_public_key': req_params.requesters_pub_key, - 'temporary_ip': incoming_msg_local_ip - }).then((ipRes)=>{ - reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); - }).finally(()=>{ - linkBackOthersLocalIPToTheirFloId(); - }); - } - break; - case "link_Others_Local_IP_To_Their_Flo_Id": - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - const req_params = res_obj.params[0]; - if(typeof req_params.requesters_pub_key !== "string") return; - let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); - if(typeof flo_addr_for_pubkey !== "string") return; - if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; - updateinDB('ipTable', { - 'flo_public_key': req_params.requesters_pub_key, - 'temporary_ip': incoming_msg_local_ip - }).then((ipRes)=>{ - reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); - }); - } - break; - } - - } - } catch(e) { - console.warn(e); - } - } - }, - connectWS() { + this.ws_connection = new WebSocket(this.ws_url); const switchMyWS = new backupSupernodesWebSocketObject(); this.ws_connection.onopen = function (evt) { - reactor.addEventListener('backup_supernode_up', function() { + reactor.addEventListener('backup_supernode_up', async function() { showMessage(`Connected to backup Supernode sever: ${evt.srcElement.url}.`); switchMyWS.updateSupernodeAvailabilityStatus(evt.srcElement.url, true); + let my_local_data = await readDB('localbitcoinUser', '00-01'); + if (typeof my_local_data == "object") { + const conn_su_flo_id = await switchMyWS.getFloIdFromWSUrl(evt.srcElement.url); + + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(my_local_data.myLocalFLOPublicKey)) { + // If conn_su_flo_id is not an immediate backup then give your data to it to sync + const myClosestSus = await readAllDB('myClosestSupernodes'); + const myClosestSusList = myClosestSus.map(m=>m.trader_flo_address); + if (myClosestSusList.indexOf(conn_su_flo_id) > + localbitcoinplusplus.master_configurations.MaxBackups) { + // Ask conn_su_flo_id to ask you your DB data + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "request_me_db_data", { + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address, + receiver_flo_address: conn_su_flo_id, + db_inst: localbitcoinplusplus.wallets.my_local_flo_address + }) + //.then(req=>doSend(req)); + } + + // Check if the number of backup ws connections satisfy max baxkup master + // config condition. If false, request ws connection to next backup supernode. + const currenctBackupWsList = Object.keys(localbitcoinplusplus.backupWS) + .filter(m=>localbitcoinplusplus.backupWS[m].ws_connection.readyState<2); + + if (currenctBackupWsList.length + < localbitcoinplusplus.master_configurations.MaxBackups) { + // Request next backup to connect + reactor.dispatchEvent('switchToBackupWSForSuperNodesOperations', evt.srcElement.url); + } + + } else { + if (typeof conn_su_flo_id == "string") { + my_local_data.lastConnectedTime = + new Date(); + my_local_data.lastConnectedSupernode = conn_su_flo_id; + updateinDB('localbitcoinUser', my_local_data); + } else { + mss = `WARNING: Failed to update current supernode connected status in localbitcoinUser.`; + showMessage(mss); + throw new Error(mss); + } + } + } }); reactor.dispatchEvent('backup_supernode_up'); }.bind(this); this.ws_connection.onclose = function (evt) { - reactor.addEventListener('backup_supernode_down', function() { + reactor.addEventListener('backup_supernode_down', async function() { showMessage(`Disconnected to backup Supernode sever: ${evt.srcElement.url}.`); switchMyWS.updateSupernodeAvailabilityStatus(evt.srcElement.url, false); + // Request next backup server to connect + await reactor.dispatchEvent('switchToBackupWSForSuperNodesOperations', evt.srcElement.url); }); reactor.dispatchEvent('backup_supernode_down'); }.bind(this); this.ws_connection.onmessage = function (evt) { - this.handle_backup_server_messages(evt); + let response = evt.data; + + let isItANodeLeavingMessage = response.search(`\\-- left`); + if(isItANodeLeavingMessage >= 0) { + reactor.dispatchEvent('fireNodeGoodByeEvent', response); + } + + let isRequestToLinkIp = response.search("linkMyLocalIPToMyFloId"); + let isRequestToLinkOthersIp = response.search("link_Others_Local_IP_To_Their_Flo_Id"); + let incoming_msg_local_ip = ``; + if (isRequestToLinkIp>=0 || isRequestToLinkOthersIp>=0) { + handle_backup_server_messages(response); + return; + } + var res_pos = response.indexOf('{'); + if (res_pos >= 0) { + var res = response.substr(res_pos); + let res_obj = JSON.parse(res); + if (res_obj.method==="add_user_public_data" + || res_obj.method==="retrieve_shamirs_secret_btc_pvtkey" + || res_obj.method==="do_you_have_latest_data_for_this_supernode" + ) { + handle_backup_server_messages(response); + return; + } + if (res_obj.method==="sync_backup_supernode_from_backup_supernode" + || res_obj.method==="sync_primary_supernode_from_backup_supernode_response" + || res_obj.method==="sync_backup_supernode_from_backup_supernode_response" + ) { + onMessage(response); + return; + } + if (res_obj.method==="is_node_alive_request" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && (res_obj.params[0].receiver_flo_address == + localbitcoinplusplus.wallets.my_local_flo_address)) { + // Register his attendance + reactor.dispatchEvent('nodeIsAlive', res_obj); + // Send your live status to Sender + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "yup_i_am_awake", { + JOB: 'I_AM_ALIVE', + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address, + receiver_flo_address: res_obj.globalParams.senderFloId + }).then(req=>doSend(req)); + return; + } + + if (res_obj.method=="yup_i_am_awake" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && (res_obj.params[0].receiver_flo_address == + localbitcoinplusplus.wallets.my_local_flo_address)) { + + reactor.dispatchEvent('nodeIsAlive', res_obj); + return; + } + + // If you want to send Shamir shares from here write the req code below + if (res_obj.method=="send_back_shamirs_secret_btc_pvtkey") { + console.log(res_obj); + return; + } + + } }.bind(this); this.ws_connection.onerror = function (evt) { console.error(evt); @@ -14148,16 +16325,31 @@ }, async updateSupernodeAvailabilityStatus(ws_url, status) { - const disconnected_su_flo_id = await this.getFloIdFromWSUrl(ws_url); + let disconnected_su_flo_id = ''; + try { + disconnected_su_flo_id = await this.getFloIdFromWSUrl(ws_url); + } catch(e) { + disconnected_su_flo_id = ws_url; // Input is a FLO Id not url + } + const get_disconnected_su_details_list = await readDBbyIndex('myClosestSupernodes', 'trader_flo_address', disconnected_su_flo_id); const get_disconnected_su_details = get_disconnected_su_details_list[0]; if(typeof get_disconnected_su_details !== "object") { showMessage(`WARNING: Failed to update status of "${ws_url}" to ${status}.`); return; } + // Code to prevent update if status is already eq to is_live value + let floId_index; + if (typeof wsUri == "object") { + floId_index = wsUri.findIndex(e=>e.trader_flo_address == disconnected_su_flo_id, disconnected_su_flo_id); + if (typeof floId_index == "number" && typeof wsUri[floId_index] == "object") { + if(wsUri[floId_index].is_live === status) return; + } + } get_disconnected_su_details.is_live = status; get_disconnected_su_details.timestamp = + new Date(); updateinDB('myClosestSupernodes', get_disconnected_su_details).then((myClosestSupernodesStatusRes)=>{ + wsUri[floId_index].is_live = status; let su_status = status === true ? 'connected' : 'disconnected'; showMessage(`INFO: Supernode ${ws_url} is now ${su_status}.`); }); @@ -14178,55 +16370,46 @@ const myClosestSupernodesArray = await readAllDB(`myClosestSupernodes`); - // Learn about the previous connection. If it was primary supernode continue - const primary_ws_connection = `ws://${myClosestSupernodesArray[0].ip}:${myClosestSupernodesArray[0].port}`; + let nextClosestSupernodeElem = myClosestSupernodesArray + .filter((wew, index)=>{ + let ww = `ws://${wew.ip}:${wew.port}`; + if(typeof z =='boolean' && z) { + z = false; + localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS = wew.trader_flo_address; + return ww; + } + if(ww==disconnected_url) z = true; + }); + + ideal_supernode = `ws://${nextClosestSupernodeElem[0].ip}:${nextClosestSupernodeElem[0].port}`; - if (primary_ws_connection !== disconnected_url) { - // If previous connection was a backup supernode, directly connect to last connected backup supernode - let status_of_last_connected_backup_su = myClosestSupernodesArray - .filter((su_list, i)=>last_connect_supernode_flo_id==su_list.trader_flo_address); - - // Get the status of last_connect_supernode - if(status_of_last_connected_backup_su[0].is_live===true) { - ideal_supernode = `ws://${status_of_last_connected_backup_su[0].ip}:${status_of_last_connected_backup_su[0].port}`; - } - } else { - // Connection to last connected backup supernode failed, - // then run below code to connect to next backup ws - - let nextClosestSupernodeElem = myClosestSupernodesArray - .filter((wew, index)=>{ - let ww = `ws://${wew.ip}:${wew.port}`; - if(typeof z =='boolean' && z) { - z = false; - localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS = wew.trader_flo_address; - return ww; - } - if(ww==disconnected_url) z = true; - }) - - ideal_supernode = `ws://${nextClosestSupernodeElem[0].ip}:${nextClosestSupernodeElem[0].port}`; - - } - await startWebSocket(ideal_supernode); if(websocket.readyState===1) { - - // Connection established, build private key and UI - await privateKeyBuilder(); - setTimeout(function(){ - if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY!=='string' - || localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length<1) { - RM_WALLET.manually_assign_my_private_key(); - loadExternalFiles(); - dataBaseUIOperations(); - clearTimeout(); - } - }, 10000); + if (!localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + + // Connection established, build private key and UI + await privateKeyBuilder(); - return Promise.resolve(true) + localbitcoinplusplus.actions.delay(10000).then(()=>{ + 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(); + } else if(typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' && + localbitcoinplusplus.is_ui_loaded == false) { + loadExternalFiles(); + dataBaseUIOperations(); + } + }); + } + + return Promise.resolve(true); } else { let ms = `Error: Failed to connect to any supernode.`; showMessage(ms) @@ -14234,6 +16417,8 @@ } }, + + } function startWebSocket(wsUri) { @@ -14256,40 +16441,28 @@ } function onOpen(evt) { - reactor.addEventListener('primary_supernode_up', function() { - const switchMyWS = new backupSupernodesWebSocketObject(); - switchMyWS.updateSupernodeAvailabilityStatus(evt.srcElement.url, true); - showMessage(`Connected successfully to Supernode: ${evt.srcElement.url}`); - writeToScreen("CONNECTED"); - }); - reactor.dispatchEvent('primary_supernode_up'); + reactor.dispatchEvent('new_supernode_connected', evt); + readDB('localbitcoinUser', '00-01').then(res=>{ + if (typeof res=="object" && res.myLocalFLOAddress=="string") { + localbitcoinplusplus.wallets.my_local_flo_address = res.myLocalFLOAddress; + localbitcoinplusplus.wallets.my_local_flo_public_key = res.myLocalFLOPublicKey; + } + }) + readAllDB('myClosestSupernodes').then(sconn=>{ + const switchMyWS = new backupSupernodesWebSocketObject(); + sconn.map((m,i)=>{ + if(i>0) switchMyWS.updateSupernodeAvailabilityStatus(m.trader_flo_address, false); + }); + }); } function onClose(evt) { - - reactor.addEventListener('primary_supernode_down', function() { - showMessage(`Disconnected to Supernode server: ${evt.srcElement.url}`); - writeToScreen("DISCONNECTED"); - - const switchMyWS = new backupSupernodesWebSocketObject(); - switchMyWS.updateSupernodeAvailabilityStatus(evt.srcElement.url, false); - showMessage(`INFO: Waiting for primary Supernode connection to come back within 20 seconds.`); - localbitcoinplusplus.actions.delay(20000).then(async ()=>{ - const disconnectedWSServerFloId = await switchMyWS.getFloIdFromWSUrl(evt.srcElement.url); - const getSubjectSupernodeDetails = await readDBbyIndex('myClosestSupernodes', 'trader_flo_address', disconnectedWSServerFloId); - if (typeof getSubjectSupernodeDetails=="object" && getSubjectSupernodeDetails[0].is_live!==true) { - showMessage(`INFO: Connection to primary Supernode failed. Attempting to connect to secondary Supernode.`); - switchMyWS.switchToBackupWS(evt.srcElement.url); - } - }); - }); - reactor.dispatchEvent('primary_supernode_down'); - + reactor.dispatchEvent('primary_supernode_down', evt); } async function onMessage(evt) { - var response = evt.data; - + var response = evt.data || evt; + console.log('RESPONSE: ' + response); // If the message is about leaving of a node determine its FLO Id // and fire respective events let isItANodeLeavingMessage = response.search(`\\-- left`); @@ -14298,18 +16471,13 @@ reactor.dispatchEvent('fireNodeGoodByeEvent', response); } - // let isItANodeJoiningMessage = response.search(`\\++ joined`); - - // if(isItANodeJoiningMessage >= 0) { - // reactor.dispatchEvent('fireNodeWelcomeBackEvent', response); - // } - var res_pos = response.indexOf('{'); if (res_pos >= 0) { // Link Temporary IP Address to FLO ID let isRequestToLinkIp = response.search("linkMyLocalIPToMyFloId"); + let isRequestToLinkOthersIp = response.search("link_Others_Local_IP_To_Their_Flo_Id"); let incoming_msg_local_ip = ``; - if (isRequestToLinkIp>=0) { + if (isRequestToLinkIp>=0 || isRequestToLinkOthersIp>=0) { let index_of_ip = response.indexOf(' '); if (index_of_ip>=0) { incoming_msg_local_ip = response.substr(0, index_of_ip); @@ -14320,11 +16488,84 @@ try { var res_obj = JSON.parse(res); + if (typeof res_obj.globalParams !== "object" + || (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && typeof res_obj.globalParams.receiversList == "object" + && !res_obj.globalParams.receiversList + .includes(localbitcoinplusplus.wallets.my_local_flo_address) + )) return; + + if (typeof res_obj.globalParams.receiverFloId=="string" + && res_obj.globalParams.receiverFloId !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + const isIncomingMessageValid = await validateIncomingMessage(res); console.log("isIncomingMessageValid: ", isIncomingMessageValid); if (!isIncomingMessageValid) return; + if(typeof res_obj.globalParams.senderFloId !=="string") + throw new Error(`WARNING: The request did not contain sender FLO Id. Request Aborted.`); + + // Check if request is from primary user or backup user + // If request is from backup user, divert the request to backup onmessage event + let get_requester_supernode = ''; + if(typeof res_obj.params[0].trader_flo_address == "string") { + get_requester_supernode = await localbitcoinplusplus.kademlia + .determineClosestSupernode(res_obj.params[0].trader_flo_address); + + res_obj.globalParams.primarySupernode = get_requester_supernode[0].data.id; + } + + if ((typeof get_requester_supernode[0] !== "object" + || typeof get_requester_supernode[0].data.id !=="string") && + typeof res_obj.globalParams.primarySupernode !=="string") { + console.log("******NEED TO ADD PRIMARY SU IN BELOW METHOD******: "); + console.log(res_obj); + } + + if(get_requester_supernode.length>0 && get_requester_supernode[0].data.id !== + localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS) { + processBackupUserOnMesssageRequest(response); + return; + } else if(typeof res_obj.globalParams.primarySupernode=="string" + && res_obj.globalParams.primarySupernode !== localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS) { + if (typeof res_obj.globalParams.receiverFloId !== 'string' + || res_obj.globalParams.receiverFloId == localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS) { + // ** Changed it for "retrieve_shamirs_secret_btc_pvtkey" when backup su is requeter + //res_obj.globalParams.receiverFloId !== localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS) { + processBackupUserOnMesssageRequest(response); + return; + } + + handle_backup_server_messages(response); + return; + } + + // Don't serve the request if data is not synced. + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + + let byPassMethods = ['sync_backup_supernode_from_backup_supernode_response', + 'sync_primary_supernode_from_backup_supernode_response', 'supernode_message', + 'store_shamirs_secret_pvtkey_shares', 'send_back_shamirs_secret_supernode_pvtkey', + 'retrieve_shamirs_secret_supernode_pvtkey', 'send_back_shamirs_secret_btc_pvtkey', + 'retrieve_shamirs_secret_btc_pvtkey', 'add_user_public_data', 'link_My_Local_IP_To_My_Flo_Id', + 'link_Others_Local_IP_To_Their_Flo_Id', 'sync_data_by_vector_clock', 'is_node_alive_request', + 'yup_i_am_awake']; + + if(!byPassMethods.includes(res_obj.method)) { + if (typeof res_obj.globalParams.primarySupernode !== "string" + // || typeof localbitcoinplusplus.services[`can_serve_${res_obj.globalParams.primarySupernode}`] !== "boolean" + // || localbitcoinplusplus.services[`can_serve_${res_obj.globalParams.primarySupernode}`]==false + ) { + showMessage(`INFO: You are not authorized to serve this request.`); + return false; + } + } + } + if (typeof res_obj.method !== "undefined") { let response_from_sever; @@ -14337,13 +16578,23 @@ if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { let received_resp = res_obj.params[0]; try { - if (received_resp.trader_flo_id.length > 0 && received_resp.server_msg.length > - 0) { - readDB("localbitcoinUser", "00-01").then(function (res) { + if (received_resp.trader_flo_address.length > 0 && received_resp.server_msg.length > 0) { + readDB("localbitcoinUser", "00-01").then(async function (res) { if (typeof res == "object" && res.myLocalFLOAddress.length > 0) { - if (res.myLocalFLOAddress === received_resp.trader_flo_id) { - showMessage(received_resp.server_msg); - return false; + if (typeof received_resp.receiver_flo_address=="string") { + if (res.myLocalFLOAddress === received_resp.receiver_flo_address) { + showMessage(received_resp.server_msg); + return false; + } + } else { + const myPrimarySu = await readDB('myClosestSupernodes', 1); + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(received_resp.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + if (primarySupernode==myPrimarySu.trader_flo_address) { + showMessage(received_resp.server_msg); + return false; + } } } }); @@ -14361,24 +16612,19 @@ if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { let buyOrders_data = res_obj.params[0]; - RM_RPC.filter_legit_requests(params.trader_flo_address, - function (is_valid_request) { - if (is_valid_request !== true) return false; - - if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && - localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( - buyOrders_data.supernodePubKey)) { - let isDataSignedBySuperNode = RM_WALLET - .verify(buyOrders_data.data_hash, buyOrders_data.supernode_sign, - buyOrders_data.supernodePubKey); - if (isDataSignedBySuperNode === true) { - // Add buy order - addDB("buyOrders", buyOrders_data).then(() => { - showMessage(`Your buy order is placed successfully.`); - }); - } + if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && + localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + buyOrders_data.supernodePubKey)) { + let isDataSignedBySuperNode = RM_WALLET + .verify(buyOrders_data.data_hash, buyOrders_data.supernode_sign, + buyOrders_data.supernodePubKey); + if (isDataSignedBySuperNode === true) { + // Add buy order + addDB("buyOrders", buyOrders_data).then(() => { + showMessage(`Your buy order is placed successfully.`); + }); } - }); + } } break; case "trade_sell": @@ -14388,24 +16634,20 @@ case "trade_sell_request_response": if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { let sellOrders_data = res_obj.params[0]; - RM_RPC.filter_legit_requests(params.trader_flo_address, - function (is_valid_request) { - if (is_valid_request !== true) return false; - if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && - localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( - sellOrders_data - .supernodePubKey)) { - let isDataSignedBySuperNode = RM_WALLET - .verify(sellOrders_data.data_hash, sellOrders_data.supernode_sign, - sellOrders_data.supernodePubKey); - if (isDataSignedBySuperNode === true) { - // Add buy order - addDB("sellOrders", sellOrders_data).then(() => { - showMessage(`Your sell order is placed successfully.`); - });; - } + if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && + localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + sellOrders_data + .supernodePubKey)) { + let isDataSignedBySuperNode = RM_WALLET + .verify(sellOrders_data.data_hash, sellOrders_data.supernode_sign, + sellOrders_data.supernodePubKey); + if (isDataSignedBySuperNode === true) { + // Add buy order + addDB("sellOrders", sellOrders_data).then(() => { + showMessage(`Your sell order is placed successfully.`); + });; } - }); + } } break; case "sync_with_supernode": @@ -14416,6 +16658,8 @@ if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { let su_db_data = res_obj.params[0]; + if(localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return; if (typeof localbitcoinplusplus.wallets.my_local_flo_address !== "string" || su_db_data.trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address ) return false; @@ -14482,8 +16726,8 @@ readDB("localbitcoinUser", "00-01").then(function (user) { if (typeof user == "object" && user.myLocalFLOAddress == resp.data.trader_flo_address) { let counterTraderAccountAddress = - `Please pay the amount to following address:
-${resp.msg}
`; + `Please pay the amount to following address: + ${resp.msg}`; showMessage(counterTraderAccountAddress); modalWindow(counterTraderAccountAddress); } @@ -14494,7 +16738,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") { @@ -14537,6 +16780,7 @@ } break; case "trade_balance_updates": + if(!localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(res_obj.nodePubKey)) return; if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { const trade_balance_res = res_obj.params[0]; // Verify data @@ -14550,40 +16794,38 @@ `${trade_info_str}${buyer_cash_data_str}${seller_cash_data_str}${buyer_btc_data_str}${seller_btc_data_str}`; let hashed_data = Crypto.SHA256(res_str); - RM_RPC.filter_legit_requests(trade_balance_res.trade_infos.buyer_flo_id, - function (is_valid_request) { - if (is_valid_request !== true) return false; + if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + trade_balance_res.supernodePubKey)) { + if (RM_WALLET.verify(hashed_data, + trade_balance_res.supernode_sign, trade_balance_res.supernodePubKey)) { + // Delete orders in clients DB + try { + removeinDB("buyOrders", trade_balance_res.trade_infos.buy_order_id); + removeinDB("sellOrders", trade_balance_res.trade_infos.sell_order_id); + } catch (error) { + callback(false); + throw new Error(error); + } - if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( - trade_balance_res.supernodePubKey)) { - if (RM_WALLET.verify(hashed_data, - trade_balance_res.supernode_sign, trade_balance_res.supernodePubKey)) { - // Delete orders in clients DB - try { - removeinDB("buyOrders", trade_balance_res.trade_infos.buy_order_id); - removeinDB("sellOrders", trade_balance_res.trade_infos.sell_order_id); - } catch (error) { - callback(false); - throw new Error(error); - } + // Update balances in clients DB + try { + updateinDB("cash_balances", trade_balance_res.buyer_cash_data, + trade_balance_res.trade_infos.buyer_flo_id, true, false); + updateinDB("cash_balances", trade_balance_res.seller_cash_data, + trade_balance_res.trade_infos.seller_flo_id, true, false); + updateinDB("crypto_balances", trade_balance_res.buyer_btc_data, + trade_balance_res.trade_infos.buyer_flo_id, true, false); + updateinDB("crypto_balances", trade_balance_res.seller_btc_data, + trade_balance_res.trade_infos.seller_flo_id, true, false); - // Update balances in clients DB - try { - updateinDB("cash_balances", trade_balance_res.buyer_cash_data, - trade_balance_res.trade_infos.buyer_flo_id); - updateinDB("cash_balances", trade_balance_res.seller_cash_data, - trade_balance_res.trade_infos.seller_flo_id); - updateinDB("crypto_balances", trade_balance_res.buyer_btc_data, - trade_balance_res.trade_infos.buyer_flo_id); - updateinDB("crypto_balances", trade_balance_res.seller_btc_data, - trade_balance_res.trade_infos.seller_flo_id); - } catch (error) { - callback(false); - throw new Error(error); - } + // Update balances + displayBalances(localbitcoinplusplus.wallets.my_local_flo_address); + } catch (error) { + callback(false); + throw new Error(error); } } - }); + } } break; case "store_shamirs_secret_pvtkey_shares": @@ -14596,19 +16838,16 @@ readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val) .then(function (res) { if (typeof res=="object") { - let send_pvtkey_req = RM_RPC + RM_RPC .send_rpc .call(this, "retrieve_shamirs_secret_supernode_pvtkey", { private_key_chunk: res - }); - doSend(send_pvtkey_req); - return; + }).then(send_pvtkey_req=>doSend(send_pvtkey_req)); } else { - let send_pvtkey_req = RM_RPC + RM_RPC .send_rpc - .call(this, "retrieve_shamirs_secret_supernode_pvtkey", ""); - doSend(send_pvtkey_req); - return; + .call(this, "retrieve_shamirs_secret_supernode_pvtkey", "") + .then(send_pvtkey_req=>doSend(send_pvtkey_req)); } }); } @@ -14638,8 +16877,8 @@ retrieve_pvtkey_counter++; } } - if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' - && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length>0 && runUIFunc==true) { + if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' + && localbitcoinplusplus.is_ui_loaded == false) { loadExternalFiles(); dataBaseUIOperations(); return; @@ -14650,14 +16889,15 @@ if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val).then(function ( res) { - let send_pvtkey_req = RM_RPC + RM_RPC .send_rpc .call(this, "retrieve_shamirs_secret_btc_pvtkey", { retrieve_pvtkey_req_id: res_obj.params[0].retrieve_pvtkey_req_id, private_key_chunk: res, - withdraw_id: res_obj.params[0].withdraw_id - }); - doSend(send_pvtkey_req); + withdraw_id: res_obj.params[0].withdraw_id, + db_inst: res_obj.params[0].db_inst, + receiver_flo_address: res_obj.globalParams.senderFloId + }).then(send_pvtkey_req=>doSend(send_pvtkey_req)); }); } break; @@ -14665,28 +16905,54 @@ if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" && typeof res_obj.params[0].private_key_chunk == "object" && typeof res_obj.params[0].retrieve_pvtkey_req_id == "string" && - typeof res_obj.params[0].withdraw_id == "string") { + typeof res_obj.params[0].withdraw_id == "string" && + typeof localbitcoinplusplus.wallets.my_local_flo_address == "string" && + res_obj.params[0].receiver_flo_address === localbitcoinplusplus.wallets.my_local_flo_address) { + + // This message was for Backup Supernode and is meant to be run in processBackupMessages() + if(res_obj.params[0].db_inst!==localbitcoinplusplus.wallets.my_local_flo_address) return; + let shamirs_shares_response = res_obj.params[0]; let retrieve_pvtkey_req_id = res_obj.params[0].retrieve_pvtkey_req_id; let withdraw_id = res_obj.params[0].withdraw_id; if (typeof btc_pvt_arr !== "object") btc_pvt_arr = []; if (typeof btc_pvt_arr[retrieve_pvtkey_req_id] == "undefined") btc_pvt_arr[ retrieve_pvtkey_req_id] = []; - btc_pvt_arr[retrieve_pvtkey_req_id].push(shamirs_shares_response); + + // Filter function below logic source - + // https://stackoverflow.com/a/9229821/5348972 + let seen_chunk_id_list = []; + + btc_pvt_arr[retrieve_pvtkey_req_id].filter(function(item) { + if (typeof item.private_key_chunk !== "undefined") { + return seen_chunk_id_list.hasOwnProperty(item.private_key_chunk.id) + ? false : (seen_chunk_id_list.push(item.private_key_chunk.id)); + } + }); + + if (!seen_chunk_id_list + .includes(shamirs_shares_response.private_key_chunk.id) + && typeof shamirs_shares_response.private_key_chunk.id == "string") { + btc_pvt_arr[retrieve_pvtkey_req_id].push(shamirs_shares_response); + } if (btc_pvt_arr[retrieve_pvtkey_req_id].length === localbitcoinplusplus.master_configurations .ShamirsMaxShares) { delete res_obj.params[0].private_key_chunk; res_obj.params[0].btc_private_key_array = JSON.stringify(btc_pvt_arr[ retrieve_pvtkey_req_id]); + res_obj.params[0].trader_flo_address = localbitcoinplusplus.wallets.my_local_flo_address; RM_RPC.receive_rpc_response.call(this, JSON.stringify(res_obj)); - btc_pvt_arr[retrieve_pvtkey_req_id] = []; // Unset the object + + localbitcoinplusplus.actions.delay(300000).then(()=>{ + btc_pvt_arr[retrieve_pvtkey_req_id] = []; // Unset the object + }); } } break; case "deposit_withdraw_user_claim": if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == - "undefined") throw new Error("Supernode Private Keys is undefind."); + "undefined") throw new Error("Supernode Private Keys is undefined."); if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { let user_claim_request = res_obj.params[0]; @@ -14756,8 +17022,7 @@ 'trader_flo_address', depositor_cash_data.trader_flo_address ); - removeinDB('withdraw_cash', - withdraw_data.id); + removeinDB('withdraw_cash', withdraw_data.id); let update_cash_balance_obj = { depositor_cash_data: depositor_cash_data, @@ -14783,13 +17048,15 @@ withdraw_data.id; update_cash_balance_obj.receiver_flo_address = user_id; + update_cash_balance_obj.trader_flo_address = user_id; - let update_cash_balance_req = RM_RPC + RM_RPC .send_rpc .call(this, "update_all_deposit_withdraw_success", - update_cash_balance_obj); - doSend(update_cash_balance_req); + update_cash_balance_obj) + .then(update_cash_balance_req=> + doSend(update_cash_balance_req)); } } else if (withdraw_data.depositor_flo_id == user_id) { @@ -14820,13 +17087,15 @@ localbitcoinplusplus.wallets.my_local_flo_public_key; update_withdraw_cash_obj_data.receiver_flo_address = user_id; + update_withdraw_cash_obj_data.trader_flo_address = user_id; - let update_withdraw_cash_obj = RM_RPC + RM_RPC .send_rpc .call(this, "update_all_withdraw_cash_depositor_claim", - update_withdraw_cash_obj_data); - doSend(update_withdraw_cash_obj); + update_withdraw_cash_obj_data) + .then(update_withdraw_cash_obj=> + doSend(update_withdraw_cash_obj)); } return true; } @@ -14863,7 +17132,8 @@ if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { let withdraw_success_response = res_obj.params[0]; let update_cash_balance_obj_res = { - depositor_cash_data: withdraw_success_response.depositor_cash_data + depositor_cash_data: withdraw_success_response.depositor_cash_data, + withdrawer_cash_data: withdraw_success_response.withdrawer_cash_data } let update_cash_balance_obj_res_str = JSON.stringify(update_cash_balance_obj_res); let update_cash_balance_obj_res_hash = Crypto.SHA256( @@ -14879,6 +17149,9 @@ removeByIndex('deposit', 'trader_flo_address', withdraw_success_response.depositor_cash_data .trader_flo_address); removeinDB('withdraw_cash', withdraw_success_response.withdraw_id); + + // Update balances + displayBalances(localbitcoinplusplus.wallets.my_local_flo_address); return true; } return false; @@ -14887,12 +17160,12 @@ case "add_user_public_data": let supernode_flo_public_key = localbitcoinplusplus.wallets.my_local_flo_public_key; - RM_RPC.filter_legit_requests(res_obj.params[0].public_data.trader_flo_address, + RM_RPC.filter_legit_requests(res_obj.params[0].trader_flo_address, function (is_valid_request) { if (is_valid_request !== true) return false; if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - let req_data = res_obj.params[0].public_data; + let req_data = res_obj.params[0]; try { let flo_address = bitjs.FLO_TEST.pubkey2address(req_data.trader_flo_pubKey); @@ -14903,11 +17176,17 @@ trader_flo_address: req_data.trader_flo_address, trader_flo_pubKey: req_data.trader_flo_pubKey, supernode_flo_public_key: supernode_flo_public_key, - trader_status: 0, + trader_reputation: 0, timestamp: +new Date() } addDB('userPublicData', public_req_object); + + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "add_user_public_data_inBackup_db", public_req_object) + .then(add_user_public_data_req=>doSend(add_user_public_data_req)); } } catch (error) { @@ -14924,7 +17203,7 @@ break; case "refresh_deposit_status_request": - RM_RPC.filter_legit_requests((is_valid_request) => { + RM_RPC.filter_legit_requests(res_obj.params[0].trader_flo_address, is_valid_request => { if (is_valid_request !== true) return false; readDBbyIndex("deposit", 'status', 1).then(function (res) { res.map(function (deposit_trade) { @@ -14938,11 +17217,11 @@ break; case "update_external_file_request": - RM_RPC.filter_legit_requests(res_obj.params[0].user_flo_address, is_valid_request => { + RM_RPC.filter_legit_requests(res_obj.params[0].trader_flo_address, is_valid_request => { if (is_valid_request !== true) return false; let update_script_request = res_obj.params[0]; - if (typeof update_script_request.user_flo_address !== "string") throw new Error( + if (typeof update_script_request.trader_flo_address !== "string") throw new Error( "Unknown user"); let server_pubkey = localbitcoinplusplus.wallets.my_local_flo_public_key; @@ -14957,16 +17236,17 @@ let server_sign = RM_WALLET .sign(file_details_string, localbitcoinplusplus.wallets .MY_SUPERNODE_PRIVATE_KEY); - response_from_sever = RM_RPC.send_rpc + RM_RPC.send_rpc .call(this, "update_external_file_server_response", { - trader_flo_address: update_script_request.user_flo_address, + trader_flo_address: update_script_request.trader_flo_address, file_updated: file_details, server_sign: server_sign, server_pubkey: server_pubkey, filename: update_script_request.file_to_update, - receiver_flo_address: update_script_request.user_flo_address - }); - doSend(response_from_sever); + trader_flo_address: update_script_request.trader_flo_address, + receiver_flo_address: update_script_request.trader_flo_address + }).then(response_from_sever=>doSend(response_from_sever)); + } }); } else { @@ -14976,16 +17256,16 @@ let server_sign = RM_WALLET .sign(file_details_str, localbitcoinplusplus.wallets .MY_SUPERNODE_PRIVATE_KEY); - response_from_sever = RM_RPC.send_rpc + RM_RPC.send_rpc .call(this, "update_external_file_server_response", { - trader_flo_address: update_script_request.user_flo_address, + trader_flo_address: update_script_request.trader_flo_address, file_updated: file_details, server_sign: server_sign, server_pubkey: server_pubkey, filename: "UPDATE_ALL_FILES", - receiver_flo_address: update_script_request.user_flo_address, - }); - doSend(response_from_sever); + receiver_flo_address: update_script_request.trader_flo_address, + trader_flo_address: update_script_request.trader_flo_address, + }).then(response_from_sever=>doSend(response_from_sever)); } }); } @@ -14999,37 +17279,63 @@ break; case "updateUserCryptoBalanceRequest": - let updateUserCryptoBalanceResponseObject = res_obj.params[0]; - let SuPubKey = readDB(userPublicData, updateUserCryptoBalanceResponseObject.trader_flo_address) - .then(user_data => { - if (typeof user_data !== "object" || user_data.supernode_flo_public_key.length < - 1) - throw new Error(`No such user exists.`); - let updateUserCryptoBalanceResponseString = JSON.stringify( - updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject); - let isBalanceLegit = RM_WALLET.verify(updateUserCryptoBalanceResponseString, - updateUserCryptoBalanceResponseObject.updatedBTCBalanceObjectSign, - user_data.supernode_flo_public_key - ); - if (isBalanceLegit) { - updateinDB("crypto_balances", updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject, - user_data.trader_flo_address); - if (localbitcoinplusplus.wallets.my_local_flo_address == - updateUserCryptoBalanceResponseObject.trader_flo_address) { - displayBalances(updateUserCryptoBalanceResponseObject.trader_flo_address); - showMessage(`INFO: Your balance is updated.`); - } - return true; - } else { - showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + let updateUserCryptoBalanceResponseObject = res_obj.params[0]; + let updateUserCryptoBalanceResponseString = JSON.stringify( + updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject); + let updateUserCryptoBalanceResponseStringHash = Crypto.SHA256(updateUserCryptoBalanceResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserCryptoBalanceResponseStringHash, + updateUserCryptoBalanceResponseObject.updatedBTCBalanceObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + updateinDB("crypto_balances", updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserCryptoBalanceResponseObject.trader_flo_address) { + displayBalances(updateUserCryptoBalanceResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); } - }); - break; + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + } + break; + + case "updateUsertraderDepositsRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + + let updateUserDepositsResponseObject = res_obj.params[0]; + let updateUserDepositsResponseString = JSON.stringify( + updateUserDepositsResponseObject.updatedTraderDepositObject); + let updateUserDepositsResponseStringHash = Crypto.SHA256(updateUserDepositsResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserDepositsResponseStringHash, + updateUserDepositsResponseObject.updatedDepositsObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + updateinDB("deposit", updateUserDepositsResponseObject.updatedTraderDepositObject); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserDepositsResponseObject.trader_flo_address) { + displayBalances(updateUserDepositsResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); + } + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + } + break; case "addNewKbucketNode": + if (!localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return; try { let mss = ''; - localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.globalParams.senderFloId) + let tid = res_obj.params[0].trader_flo_address || res_obj.globalParams.senderFloId; + localbitcoinplusplus.kademlia.determineClosestSupernode(tid) .then(async my_closest_su=>{ const newKbucketObjectObj = res_obj.params[0]; @@ -15038,24 +17344,18 @@ newKbucketObject_id_array = Object.values(newKbucketObject.id); newKbucketObject_idu8 = new Uint8Array(newKbucketObject_id_array); - if (localbitcoinplusplus.wallets.my_local_flo_address !== my_closest_su[0].data.id) { - // User is connected to backup supernode - localbitcoinplusplus.kademlia.addNewUserNodeInKbucket("FLO_TEST", + + localbitcoinplusplus.kademlia.addNewUserNodeInKbucketAndDB("FLO_TEST", newKbucketObject_idu8, newKbucketObject.data); - } else { - // User is connected to primary supernode - localbitcoinplusplus.kademlia.addNewUserNodeInKbucketAndDB("FLO_TEST", - newKbucketObject_idu8, newKbucketObject.data); - } - let removeRedundantKNode = localbitcoinplusplus.rpc.prototype + localbitcoinplusplus.rpc.prototype .send_rpc .call(this, "requestSupernodesToRemoveAUserFloIdFromTheirKBucket", { redundantKbucketNodeU8Id: newKbucketObject_idu8, - currentSupernodeFloId: localbitcoinplusplus.wallets.my_local_flo_address - }); - doSend(removeRedundantKNode); - + currentSupernodeFloId: localbitcoinplusplus.wallets.my_local_flo_address, + trader_flo_address: res_obj.globalParams.senderFloId, + }).then(removeRedundantKNode=>doSend(removeRedundantKNode)); + } else { mss = `WARNING: Failed to add ${res_obj.globalParams.senderFloId} to KBucket.`; showMessage(mss) @@ -15085,21 +17385,21 @@ break; case "link_My_Local_IP_To_My_Flo_Id": - if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { - const req_params = res_obj.params[0]; - if(typeof req_params.requesters_pub_key !== "string") return; - let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); - if(typeof flo_addr_for_pubkey !== "string") return; - if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; - updateinDB('ipTable', { - 'flo_public_key': req_params.requesters_pub_key, - 'temporary_ip': incoming_msg_local_ip - }).then((ipRes)=>{ - reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); - }).finally(()=>{ - linkBackOthersLocalIPToTheirFloId(); - }); - } + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const req_params = res_obj.params[0]; + if(typeof req_params.requesters_pub_key !== "string") return; + let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); + if(typeof flo_addr_for_pubkey !== "string") return; + if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; + updateinDB('ipTable', { + 'flo_public_key': req_params.requesters_pub_key, + 'temporary_ip': incoming_msg_local_ip + }).then((ipRes)=>{ + reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); + }).finally(()=>{ + linkBackOthersLocalIPToTheirFloId(); + }); + } break; case "link_Others_Local_IP_To_Their_Flo_Id": @@ -15124,7 +17424,8 @@ // if (is_valid_request === true) { let data = res_obj.params[0]; const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", - "crypto_balances", "cash_balances", "userPublicData" + "crypto_balances", "cash_balances", "userPublicData", + "buyOrders", "sellOrders" ]; localbitcoinplusplus.actions.get_sharable_db_data(tableArray) .then(function (su_db_data) { @@ -15163,9 +17464,6 @@ su_db_data.trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address ) return false; - // const BACKUP_DB = new newBackupDB(); - // BACKUP_DB.createNewDB(); - (async function () { for (let tableStoreName in su_db_data) { // skip loop if the property is from prototype @@ -15184,12 +17482,12 @@ } } } else { - let resdbdata = await BACKUP_DB.backup_removeAllinDB(tableStoreName); if (resdbdata !== false) { if (obj.length > 0) { for (var prop in obj) { if (!obj.hasOwnProperty(prop)) continue; - await BACKUP_DB.backup_addDB(resdbdata, obj[prop]); + await BACKUP_DB.backup_updateinDB(resdbdata, obj[prop], obj[ + prop].trader_flo_address, true, false); } } } @@ -15227,6 +17525,258 @@ console.log(res_obj); break; + case "sync_data_by_vector_clock": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + try { + + (async function() { + + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(res_obj.params[0].trader_flo_address); + + let primarySupernode = getPrimarySuObj[0].data.id; + + if (localbitcoinplusplus.wallets.my_local_flo_address!==primarySupernode) return; + + let req_dt = res_obj.params[0]; + let dbTable = req_dt.dbTable; + let data = req_dt.data; + let subjectUser = data.trader_flo_address; + let mss = ''; + + if (typeof data.id !== "string" && typeof data.id !== "number") { + mss = `WARNING: Failed to sync data by vector clock as id field could not be found.`; + showMessage(mss); + throw new Error(mss); + } + let myOwnDBData = await readDB(dbTable, data.id); + if (typeof myOwnDBData.vectorClock !== "number") { + mss = `WARNING: Failed to sync data by vector clock as id field could not be found.`; + showMessage(mss); + throw new Error(mss); + } + if(data.vectorClock > myOwnDBData.vectorClock) { + // You have old data, update respective DB. + updateinDB(dbTable, data, data.id, false, false).then(()=>{ + showMessage(`INFO: Data updated in ${dbTable} for id ${data.id}.`); + }); + } + + })() + } catch (error) { + throw new Error(error); + } + + } + } + 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; + if (!localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return; + + (async function () { + let i = 0; + for (let tableStoreName in su_db_data) { + i++; + + // 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 (obj.length > 0) { + for (var prop in obj) { + if (!obj.hasOwnProperty(prop)) continue; + await updateinDB(tableStoreName, obj[prop], obj[prop].id, true, false) + .then(()=>{ + showMessage(`INFO: "${tableStoreName}" datastore syncing is complete.`); + }); + } + } + + if (i==Object.keys(su_db_data).length-2) { + localbitcoinplusplus.services[`can_serve_${su_db_data.trader_flo_address}`] = true; + + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "supernode_message", { + "trader_flo_address": su_db_data.trader_flo_address, + "server_msg": `System is synced and ready to serve.` + }).then(server_response=>doSend(server_response)); + } + + } catch (error) { + console.log(error); + } + } + })(); + } + break; + + case "sync_backup_supernode_from_backup_supernode_response": + + let su_db_data = res_obj.params[0]; + if (typeof localbitcoinplusplus.wallets.my_local_flo_address !== "string" + || typeof su_db_data.receiver_flo_address !== "string" + || su_db_data.receiver_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address){ + console.warn(`WARNING: This backup response data was not meant for you.`); + return; + } + + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + + (async function () { + let _addDB = addDB; + let _removeAllinDB = removeAllinDB; + let _updateinDB = updateinDB; + let backup_db; + + if(typeof localbitcoinplusplus.wallets.my_local_flo_address !=="string" + || localbitcoinplusplus.wallets.my_local_flo_address.length<1) { + console.warn(`WARNING: Local FLO Address is not set.`); + return; + } + + if (su_db_data.trader_flo_address !==localbitcoinplusplus.wallets.my_local_flo_address) { + backup_db = su_db_data.trader_flo_address; + } + 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]; + _readDB = foreign_db.backup_readDB.bind(foreign_db); + _readDBbyIndex = foreign_db.backup_readDBbyIndex.bind(foreign_db); + _updateinDB = foreign_db.backup_updateinDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } + let i = 0; + for (let tableStoreName in su_db_data) { + i++; + if (i==Object.keys(su_db_data).length-2) { + localbitcoinplusplus.services[`can_serve_${su_db_data[`trader_flo_address`]}`] = true; + // Close unnecessary connections now + reactor.dispatchEvent('remove_extra_backup_connections'); + } + // 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 (obj.length > 0) { + for (var prop in obj) { + if (!obj.hasOwnProperty(prop)) continue; + _updateinDB(tableStoreName, obj[prop], obj[prop].id, true, false) + .then(()=>{ + showMessage(`INFO: "${tableStoreName}" datastore syncing is complete.`); + }); + } + } + + } catch (error) { + console.log(error); + } + } + })(); + } + break; + + case "reconnect_with_another_supernode": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" + && !localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + ) { + (async function() { + let su_db_data = res_obj.params[0]; + const MCS = await localbitcoinplusplus.kademlia + .determineClosestSupernode(localbitcoinplusplus.wallets.my_local_flo_address, 10); + + // If user is already connected to primary return back + if (localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS == MCS[0].data.id) return; + + // If user is already connected to given Supernode return + if (localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS == su_db_data.trader_flo_address) return; + + const mcslist = MCS.map(m=>m.data.id); + + if(mcslist.indexOf(su_db_data.trader_flo_address) < mcslist.indexOf(localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS)) { + if (websocket.readyState === WebSocket.OPEN) { + websocket.close(); + const newSu = await readDBbyIndex('myClosestSupernodes','trader_flo_address', su_db_data.trader_flo_address); + if (typeof newSu=="object" && typeof newSu[0].trader_flo_address=="string") { + await startWebSocket(`ws://${newSu[0].ip}:${newSu[0].port}`); + showMssage(`INFO: ${su_db_data.server_msg}`); + } else { + alert(`INFO: Please reload the page.`); + } + } + } + + })(); + } + break; + + case "is_node_alive_request": + if(localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + reactor.dispatchEvent('nodeIsAlive', res_obj); + // Send your live status to Sender + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "yup_i_am_awake", { + JOB: 'I_AM_ALIVE', + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address, + receiver_flo_address: res_obj.globalParams.senderFloId + }).then(req=>doSend(req)); + } + break; + + case "yup_i_am_awake": + if (res_obj.method=="yup_i_am_awake" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && (res_obj.params[0].receiver_flo_address == + localbitcoinplusplus.wallets.my_local_flo_address)) { + + reactor.dispatchEvent('nodeIsAlive', res_obj); + return; + } + break; + + case "you_are_set_to_serve_given_supernode": + if (res_obj.method=="you_are_set_to_serve_given_supernode" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && (res_obj.params[0].receiver_flo_address == + localbitcoinplusplus.wallets.my_local_flo_address)) { + + let supernode_to_serve = localbitcoinplusplus + .services[`can_serve_${res_obj.params[0].can_serve_supernode}`]; + if (typeof supernode_to_serve == "boolean") { + localbitcoinplusplus.services[`can_serve_${res_obj.params[0].can_serve_supernode}`] = true; + } + } + break; + default: break; } @@ -15236,7 +17786,2740 @@ return; } } - writeToScreen('RESPONSE: ' + evt.data + ''); + } + + async function processBackupUserOnMesssageRequest(response) { + console.log('processBackupUserOnMesssageRequest RESPONSE: ' + response); + // If the message is about leaving of a node determine its FLO Id + // and fire respective events + let isItANodeLeavingMessage = response.search(`\\-- left`); + + if(isItANodeLeavingMessage >= 0) { + reactor.dispatchEvent('fireNodeGoodByeEvent', response); + } + + var res_pos = response.indexOf('{'); + if (res_pos >= 0) { + // Link Temporary IP Address to FLO ID + let isRequestToLinkIp = response.search("linkMyLocalIPToMyFloId"); + let isRequestToLinkOthersIp = response.search("link_Others_Local_IP_To_Their_Flo_Id"); + let incoming_msg_local_ip = ``; + if (isRequestToLinkIp>=0 || isRequestToLinkOthersIp>=0) { + let index_of_ip = response.indexOf(' '); + if (index_of_ip>=0) { + incoming_msg_local_ip = response.substr(0, index_of_ip); + } + } + + var res = response.substr(res_pos); + try { + var res_obj = JSON.parse(res); + + if (typeof res_obj.globalParams !== "object" + || (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && typeof res_obj.globalParams.receiversList == "object" + && !res_obj.globalParams.receiversList + .includes(localbitcoinplusplus.wallets.my_local_flo_address) + )) return; + + if (typeof res_obj.globalParams.receiverFloId=="string" + && res_obj.globalParams.receiverFloId !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + + // Check if request is from primary user or backup user + // If request is from backup user, divert the request to backup onmessage event + let get_requester_supernode = ''; + if(typeof res_obj.params[0].trader_flo_address == "string") { + get_requester_supernode = await localbitcoinplusplus.kademlia + .determineClosestSupernode(res_obj.params[0].trader_flo_address); + + res_obj.globalParams.primarySupernode = get_requester_supernode[0].data.id; + } else if(typeof res_obj.globalParams.primarySupernode !== "string") { + console.log("---- ('processBackupUserOnMesssageRequest'): NEED TO ADD PRIMARY SU IN BELOW METHOD: "); + console.log(res_obj); + return; + } + + if (res_obj.method !== "sync_primary_supernode_from_backup_supernode" + && res_obj.method !== "sync_backup_supernode_from_backup_supernode" + && res_obj.method !== "link_My_Local_IP_To_My_Flo_Id" + && res_obj.method !== "link_Others_Local_IP_To_Their_Flo_Id" + && res_obj.method !== "send_back_shamirs_secret_btc_pvtkey" + && res_obj.method !== "send_back_shamirs_secret_supernode_pvtkey" + && res_obj.method !== "store_shamirs_secret_pvtkey_shares" + && res_obj.method !== "retrieve_shamirs_secret_supernode_pvtkey" + ) { + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + + const subject_user = res_obj.params[0].trader_flo_address + || res_obj.globalParams.primarySupernode; + + let ifAllPrevSuAreDead = await localbitcoinplusplus.actions + .checkIfAllPreviousSupernodesAreDeadForAUserNode(subject_user); + + console.log("ifAllPrevSuAreDead: ", ifAllPrevSuAreDead); + + let allowed_methods = ['sync_backup_supernode_from_backup_supernode_response', + 'yup_i_am_awake', "updateUserBTCReservesRequest"]; + + if (ifAllPrevSuAreDead !== true || allowed_methods.includes(res_obj.method)) { + handle_backup_server_messages(response); + return; + } + } + } + + // Don't serve the request if data is not synced. + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + + let byPassMethods = ['sync_backup_supernode_from_backup_supernode_response', + 'sync_primary_supernode_from_backup_supernode_response', 'supernode_message', + 'store_shamirs_secret_pvtkey_shares', 'send_back_shamirs_secret_supernode_pvtkey', + 'retrieve_shamirs_secret_supernode_pvtkey', 'send_back_shamirs_secret_btc_pvtkey', + 'retrieve_shamirs_secret_btc_pvtkey', 'add_user_public_data', 'link_My_Local_IP_To_My_Flo_Id', + 'link_Others_Local_IP_To_Their_Flo_Id', 'sync_data_by_vector_clock', 'is_node_alive_request', + 'yup_i_am_awake']; + + if(!byPassMethods.includes(res_obj.method)) { + if (typeof res_obj.globalParams.primarySupernode !== "string" + // || typeof localbitcoinplusplus.services[`can_serve_${res_obj.globalParams.primarySupernode}`] !== "boolean" + // || localbitcoinplusplus.services[`can_serve_${res_obj.globalParams.primarySupernode}`]==false + ) { + showMessage(`INFO: You are not authorized to serve this request.`); + return false; + } + } + } + + const isIncomingMessageValid = await validateIncomingMessage(res); + console.log("isIncomingMessageValid: ", isIncomingMessageValid); + + if (!isIncomingMessageValid) return; + + if (typeof res_obj.method !== "undefined") { + let response_from_sever; + + const RM_WALLET = new localbitcoinplusplus.wallets; + const RM_TRADE = new localbitcoinplusplus.trade; + const RM_RPC = new localbitcoinplusplus.rpc; + + switch (res_obj.method) { + case "supernode_message": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let received_resp = res_obj.params[0]; + + if(typeof res_obj.params[0].trader_flo_address =="string") return; + // Only the relevent user node should get response + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + try { + if (received_resp.trader_flo_address.length > 0 && received_resp.server_msg.length > 0) { + readDB("localbitcoinUser", "00-01").then(async function (res) { + if (typeof res == "object" && res.myLocalFLOAddress.length > 0) { + if (res.myLocalFLOAddress === received_resp.trader_flo_address) { + showMessage(received_resp.server_msg); + return false; + } + } else { + const myPrimarySu = await readDB('myClosestSupernodes', 1); + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(received_resp.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + if (primarySupernode==myPrimarySu.trader_flo_address) { + showMessage(received_resp.server_msg); + return false; + } + } + }); + } + } catch (error) { + throw new Error(error); + } + } + break; + case "trade_buy": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + break; + case "trade_buy_request_response": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let buyOrders_data = res_obj.params[0]; + + // Only the relevent user node should get response + if(buyOrders_data.trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" + // Commented this because it prevents usernode to register data in db + // while a backup supernode sends it any data like trade_buy + + // && localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + // buyOrders_data.supernodePubKey) + ) { + let isDataSignedBySuperNode = RM_WALLET + .verify(buyOrders_data.data_hash, buyOrders_data.supernode_sign, + buyOrders_data.supernodePubKey); + if (isDataSignedBySuperNode === true) { + // Add buy order + addDB("buyOrders", buyOrders_data).then(() => { + showMessage(`Your buy order is placed successfully.`); + }); + } + } + } + break; + case "trade_sell": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + break; + case "trade_sell_request_response": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let sellOrders_data = res_obj.params[0]; + // Only the relevent user node should get response + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" + // Commented this because it prevents usernode to register data in db + // while a backup supernode sends it any data like trade_buy + + // && localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + // sellOrders_data.supernodePubKey) + ) { + let isDataSignedBySuperNode = RM_WALLET + .verify(sellOrders_data.data_hash, sellOrders_data.supernode_sign, + sellOrders_data.supernodePubKey); + if (isDataSignedBySuperNode === true) { + // Add buy order + addDB("sellOrders", sellOrders_data).then(() => { + showMessage(`Your sell order is placed successfully.`); + });; + } + } + } + break; + case "sync_with_supernode": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + break; + case "server_sync_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 + || localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + ) return false; + // Only the relevent user node should get response + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + (async function () { + for (let tableStoreName in su_db_data) { + // skip loop if the property is from prototype + if (tableStoreName == 'trader_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); + } + } + } 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]); + } + } + } + } + + } catch (error) { + console.log(error); + } + } + })(); + + // Pass data to build_deposit_withdraw_table function + try { + console.log(su_db_data.withdraw_cash); + localbitcoinplusplus.actions.build_deposit_withdraw_table(su_db_data.withdraw_cash); + } catch (error) { + console.error(error); + } + + } + break; + case "deposit_asset_request": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + case "deposit_asset_request_response": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" && typeof res_obj + .params[0].data == "object") { + // Only the relevent user node should get response + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + let resp = res_obj.params[0]; + if (RM_WALLET + .verify(resp.data.depositDataHash, resp.data.order_validator_sign, resp.data.order_validator_public_key) + ) { + addDB('deposit', resp.data); + if (typeof resp.withdrawer_data == "object") { + updateinDB("withdraw_cash", resp.withdrawer_data, resp.withdrawer_data.trader_flo_address); + } + readDB("localbitcoinUser", "00-01").then(function (user) { + if (typeof user == "object" && user.myLocalFLOAddress == resp.data.trader_flo_address) { + let counterTraderAccountAddress = + `Please pay the amount to following address:
+${resp.msg}
`; + showMessage(counterTraderAccountAddress); + modalWindow(counterTraderAccountAddress); + } + }); + } + } + break; + case "withdraw_request_method": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); // send response to client + break; + case "withdrawal_request_response": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + if (RM_WALLET + .verify(res_obj.params[0].withdrawDataHash, res_obj.params[0].order_validator_sign, + res_obj.params[0].order_validator_public_key)) { + + addDB('withdraw_cash', res_obj.params[0]).then(() => { + showMessage(`Your cash withdrawal request is placed successfully.`); + }); + } + } + break; + case "cancel_trade": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let cancel_request = res_obj.params[0]; + if (cancel_request.job == "cancel_trade_request") { + + 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; + 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("userPublicData", cancel_request.trader_flo_address).then((trader_data) => { + if (typeof trader_data.trader_flo_address !== "string" || typeof trader_data + .trader_flo_pubKey !== "string") { + err_msg="ERROR: Failed to cancel the trade. User is unknown."; + showMessage(err_msg); + throw new Error(err_msg); + } + tradeDB = cancel_request.trade_type == "buy" ? "buyOrders" : "sellOrders"; + if (RM_WALLET + .verify(cancel_request.trade_id, cancel_request.signed_trade_id, + trader_data.trader_flo_pubKey)) { + backup_server_db_instance.backup_removeinDB(tradeDB, cancel_request.trade_id) + .then((id) =>showMessage(`Trade Id ${id} deleted.`)); + } else { + showMessage( + `Failed to verify trade for trade id ${cancel_request.trade_id}` + ); + } + }) + }) + } else { + showMessage("Failed to cancel trade."); + } + } + break; + case "trade_balance_updates": + if(!localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(res_obj.nodePubKey)) return; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const trade_balance_res = res_obj.params[0]; + // Verify data + let trade_info_str = JSON.stringify(trade_balance_res.trade_infos); + let buyer_cash_data_str = JSON.stringify(trade_balance_res.buyer_cash_data); + let seller_cash_data_str = JSON.stringify(trade_balance_res.seller_cash_data); + let buyer_btc_data_str = JSON.stringify(trade_balance_res.buyer_btc_data); + let seller_btc_data_str = JSON.stringify(trade_balance_res.seller_btc_data); + + let res_str = + `${trade_info_str}${buyer_cash_data_str}${seller_cash_data_str}${buyer_btc_data_str}${seller_btc_data_str}`; + let hashed_data = Crypto.SHA256(res_str); + + // if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + // trade_balance_res.supernodePubKey)) { + if (RM_WALLET.verify(hashed_data, + trade_balance_res.supernode_sign, trade_balance_res.supernodePubKey)) { + + // Delete orders in clients DB + try { + removeinDB("buyOrders", trade_balance_res.trade_infos.buy_order_id); + removeinDB("sellOrders", trade_balance_res.trade_infos.sell_order_id); + } catch (error) { + callback(false); + throw new Error(error); + } + + // Update balances in clients DB + try { + updateinDB("cash_balances", trade_balance_res.buyer_cash_data, + trade_balance_res.trade_infos.buyer_flo_id, true, false); + updateinDB("cash_balances", trade_balance_res.seller_cash_data, + trade_balance_res.trade_infos.seller_flo_id, true, false); + updateinDB("crypto_balances", trade_balance_res.buyer_btc_data, + trade_balance_res.trade_infos.buyer_flo_id, true, false); + updateinDB("crypto_balances", trade_balance_res.seller_btc_data, + trade_balance_res.trade_infos.seller_flo_id, true, false); + + // Update balances + displayBalances(localbitcoinplusplus.wallets.my_local_flo_address); + } catch (error) { + callback(false); + throw new Error(error); + } + } + //} + } + break; + case "store_shamirs_secret_pvtkey_shares": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + // 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; + // 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); + // }; + + // }); + delete res_obj.params[0].trader_flo_address; + addDB("supernode_private_key_chunks", res_obj.params[0]); + } + } + break; + case "send_back_shamirs_secret_supernode_pvtkey": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + ) { + + if(typeof res_obj.globalParams.primarySupernode !="string") return; + localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.globalParams.primarySupernode) + .then(my_closest_su_list=>{ + console.log(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) { + 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)); + } else { + RM_RPC + .send_rpc + .call(this, "retrieve_shamirs_secret_supernode_pvtkey", "") + .then(send_pvtkey_req=>doSend(send_pvtkey_req)); + } + + }); + }); + } + break; + case "retrieve_shamirs_secret_supernode_pvtkey": + + if(typeof retrieve_pvtkey_counter=="undefined") retrieve_pvtkey_counter = 0; + let runUIFunc = false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" && + typeof res_obj.params[0].private_key_chunk == "object" && + typeof localbitcoinplusplus.wallets.supernode_transaction_key == "object") { + + let share = res_obj.params[0].private_key_chunk.privateKeyChunks; + if (typeof share !== "undefined" && !MY_PRIVATE_KEY_SHAMIRS_SHARES.includes(share)) { + MY_PRIVATE_KEY_SHAMIRS_SHARES.push(share); + } + if (MY_PRIVATE_KEY_SHAMIRS_SHARES.length == 5) { + RM_WALLET.rebuild_my_private_key(localbitcoinplusplus.wallets.supernode_transaction_key); + runUIFunc = true; + } + } else { + if (retrieve_pvtkey_counter==10 + && typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == "undefined" + ) { + RM_WALLET.manually_assign_my_private_key(); + runUIFunc = true; + retrieve_pvtkey_counter++; + } + } + if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' && + localbitcoinplusplus.is_ui_loaded == false) { + loadExternalFiles(); + dataBaseUIOperations(); + return; + } + retrieve_pvtkey_counter++; + break; + case "send_back_shamirs_secret_btc_pvtkey": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + + if(typeof res_obj.globalParams.primarySupernode !="string") return; + + readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val).then(function ( + res) { + RM_RPC + .send_rpc + .call(this, "retrieve_shamirs_secret_btc_pvtkey", { + retrieve_pvtkey_req_id: res_obj.params[0].retrieve_pvtkey_req_id, + private_key_chunk: res, + withdraw_id: res_obj.params[0].withdraw_id, + db_inst: res_obj.params[0].db_inst, + receiver_flo_address: res_obj.globalParams.senderFloId, + }).then(send_pvtkey_req=>doSend(send_pvtkey_req)); + }); + } + break; + case "retrieve_shamirs_secret_btc_pvtkey": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" && + typeof res_obj.params[0].private_key_chunk == "object" && + typeof res_obj.params[0].retrieve_pvtkey_req_id == "string" && + typeof res_obj.params[0].withdraw_id == "string" && + typeof res_obj.params[0].db_inst == "string" && + typeof localbitcoinplusplus.wallets.my_local_flo_address == "string" && + res_obj.params[0].receiver_flo_address === localbitcoinplusplus.wallets.my_local_flo_address) { + + // This message was for Primary Supernode and is meant to be run in onMessage() + if(res_obj.params[0].db_inst==localbitcoinplusplus.wallets.my_local_flo_address) return; + + let shamirs_shares_response = res_obj.params[0]; + let retrieve_pvtkey_req_id = res_obj.params[0].retrieve_pvtkey_req_id; + let withdraw_id = res_obj.params[0].withdraw_id; + if (typeof btc_pvt_arr !== "object") btc_pvt_arr = []; + if (typeof btc_pvt_arr[retrieve_pvtkey_req_id] == "undefined") btc_pvt_arr[ + retrieve_pvtkey_req_id] = []; + // Filter function below logic source - + // https://stackoverflow.com/a/9229821/5348972 + let seen_chunk_id_list = []; + + btc_pvt_arr[retrieve_pvtkey_req_id].filter(function(item) { + return seen_chunk_id_list.hasOwnProperty(item.private_key_chunk.id) + ? false : (seen_chunk_id_list.push(item.private_key_chunk.id)); + }); + + if (!seen_chunk_id_list + .includes(shamirs_shares_response.private_key_chunk.id) + && typeof shamirs_shares_response.private_key_chunk.id == "string") { + btc_pvt_arr[retrieve_pvtkey_req_id].push(shamirs_shares_response); + } + if (btc_pvt_arr[retrieve_pvtkey_req_id].length === localbitcoinplusplus.master_configurations + .ShamirsMaxShares) { + delete res_obj.params[0].private_key_chunk; + res_obj.params[0].btc_private_key_array = JSON.stringify(btc_pvt_arr[ + retrieve_pvtkey_req_id]); + res_obj.params[0].trader_flo_address = localbitcoinplusplus.wallets.my_local_flo_address; + RM_RPC.backup_receive_rpc_response.call(this, JSON.stringify(res_obj)); + + localbitcoinplusplus.actions.delay(300000).then(()=>{ + btc_pvt_arr[retrieve_pvtkey_req_id] = []; // Unset the object + }); + } + } + break; + case "deposit_withdraw_user_claim": + + if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == + "undefined") throw new Error("Supernode Private Keys is undefind."); + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == + "object") { + let user_claim_request = res_obj.params[0]; + let user_claim_id = user_claim_request.claim_id.split('!!'); + let withdraw_order_id = user_claim_id[0]; + let user_id = user_claim_id[1]; + + RM_RPC.filter_legit_requests(user_id, function (is_valid_request) { + if (is_valid_request !== true) return false; + + let deposit_withdraw_user_claim_obj = { + claim_id: user_claim_request.claim_id + } + + let deposit_withdraw_user_claim_str = JSON.stringify( + deposit_withdraw_user_claim_obj); + let deposit_withdraw_user_claim_hash = Crypto.SHA256( + deposit_withdraw_user_claim_str); + + if (deposit_withdraw_user_claim_hash == user_claim_request.hash && + RM_WALLET.verify(deposit_withdraw_user_claim_hash, + user_claim_request.sign, user_claim_request.userPubKey)) { + //If the request is valid, find out if the requester is depositor or withdrawer + + 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; + 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("withdraw_cash", withdraw_order_id).then(async function ( + withdraw_data) { + if (typeof withdraw_data == "object") { + if (withdraw_data.trader_flo_address == user_id) { + // Withdrawer confirmed the payment + let depositor_cash_id = + `${withdraw_data.depositor_flo_id}_${withdraw_data.currency}`; + let withdrawer_cash_id = + `${withdraw_data.trader_flo_address}_${withdraw_data.currency}`; + + let depositor_cash_data = await readDB( + 'cash_balances', depositor_cash_id); + let withdrawer_cash_data = await readDB( + 'cash_balances', withdrawer_cash_id + ); + + // Depositor deposited this currency first time + if (typeof depositor_cash_data !== "object" || + typeof depositor_cash_data == + "undefined") { + depositor_cash_data = { + id: depositor_cash_id, + cash_balance: 0, + trader_flo_address: withdraw_data + .depositor_flo_id, + currency: withdraw_data.currency + }; + backup_server_db_instance.backup_addDB('cash_balances', + depositor_cash_data); + } + if (typeof depositor_cash_data == "object" && + typeof withdrawer_cash_data == "object" + ) { + depositor_cash_data.cash_balance += + parseFloat(withdraw_data.withdraw_amount); + withdrawer_cash_data.cash_balance -= + parseFloat(withdraw_data.withdraw_amount); + backup_server_db_instance.backup_updateinDB('cash_balances', + depositor_cash_data); + backup_server_db_instance.backup_updateinDB('cash_balances', + withdrawer_cash_data); + backup_server_db_instance.backup_removeByIndex('deposit', + 'trader_flo_address', + depositor_cash_data.trader_flo_address + ); + backup_server_db_instance.backup_removeinDB('withdraw_cash', withdraw_data.id); + + let update_cash_balance_obj = { + depositor_cash_data: depositor_cash_data, + withdrawer_cash_data: withdrawer_cash_data + } + let update_cash_balance_str = JSON.stringify( + update_cash_balance_obj); + let update_cash_balance_hash = Crypto.SHA256( + update_cash_balance_str); + let update_cash_balance_sign = + RM_WALLET + .sign(update_cash_balance_hash, + localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY + ); + + update_cash_balance_obj.publicKey = + localbitcoinplusplus.wallets.my_local_flo_public_key; + update_cash_balance_obj.sign = + update_cash_balance_sign; + update_cash_balance_obj.hash = + update_cash_balance_hash; + update_cash_balance_obj.withdraw_id = + withdraw_data.id; + + update_cash_balance_obj.receiver_flo_address = user_id; + + RM_RPC + .send_rpc + .call(this, + "update_all_deposit_withdraw_success", + update_cash_balance_obj) + .then(update_cash_balance_req=> + doSend(update_cash_balance_req)); + } + } + else if (withdraw_data.depositor_flo_id == user_id) { + // Depositor claimed to deposit the cash + withdraw_data.status = 3; + backup_server_db_instance.backup_updateinDB('withdraw_cash', withdraw_data, + withdraw_data.id); + let update_withdraw_cash_obj_data = { + depositor_claim: withdraw_data + }; + let update_withdraw_cash_obj_data_str = + JSON.stringify( + update_withdraw_cash_obj_data); + let update_withdraw_cash_obj_data_hash = + Crypto.SHA256( + update_withdraw_cash_obj_data_str); + let update_withdraw_cash_obj_data_sign = + RM_WALLET + .sign( + update_withdraw_cash_obj_data_hash, + localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY + ); + update_withdraw_cash_obj_data.hash = + update_withdraw_cash_obj_data_hash; + update_withdraw_cash_obj_data.sign = + update_withdraw_cash_obj_data_sign; + update_withdraw_cash_obj_data.publicKey = + localbitcoinplusplus.wallets.my_local_flo_public_key; + + update_withdraw_cash_obj_data.receiver_flo_address = user_id; + + RM_RPC + .send_rpc + .call(this, + "update_all_withdraw_cash_depositor_claim", + update_withdraw_cash_obj_data) + .then(update_withdraw_cash_obj=> + doSend(update_withdraw_cash_obj)); + } + } + }); + }); + + } + }); + + } + break; + case "update_all_withdraw_cash_depositor_claim": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + + // Only the relevent user node should get response + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + let depositor_claim_response_object = res_obj.params[0]; + let update_withdraw_cash_obj_data_res = { + depositor_claim: depositor_claim_response_object.depositor_claim + }; + let update_withdraw_cash_obj_data_res_str = JSON.stringify( + update_withdraw_cash_obj_data_res); + let depositor_claim_response_data_hash = Crypto.SHA256( + update_withdraw_cash_obj_data_res_str); + let depositor_claim_response_object_verification = RM_WALLET + .verify(depositor_claim_response_data_hash, depositor_claim_response_object.sign, + depositor_claim_response_object.publicKey); + + if ((depositor_claim_response_data_hash == depositor_claim_response_object.hash) && + (depositor_claim_response_object_verification == true)) { + + updateinDB('withdraw_cash', depositor_claim_response_object.depositor_claim, + depositor_claim_response_object.depositor_claim.id); + return true; + } + return false; + } + break; + case "update_all_deposit_withdraw_success": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let withdraw_success_response = res_obj.params[0]; + // Only the relevent user node should get response + if(res_obj.params[0].trader_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + let update_cash_balance_obj_res = { + depositor_cash_data: withdraw_success_response.depositor_cash_data, + withdrawer_cash_data: withdraw_success_response.withdrawer_cash_data + } + let update_cash_balance_obj_res_str = JSON.stringify(update_cash_balance_obj_res); + let update_cash_balance_obj_res_hash = Crypto.SHA256( + update_cash_balance_obj_res_str); + let update_cash_balance_obj_res_verification = RM_WALLET + .verify(update_cash_balance_obj_res_hash, withdraw_success_response.sign, + withdraw_success_response.publicKey); + + if ((update_cash_balance_obj_res_hash == withdraw_success_response.hash) && + update_cash_balance_obj_res_verification == true) { + + updateinDB('cash_balances', withdraw_success_response.depositor_cash_data); + updateinDB('cash_balances', withdraw_success_response.withdrawer_cash_data); + removeByIndex('deposit', 'trader_flo_address', withdraw_success_response.depositor_cash_data + .trader_flo_address); + removeinDB('withdraw_cash', withdraw_success_response.withdraw_id); + + // Update balances + displayBalances(localbitcoinplusplus.wallets.my_local_flo_address); + return true; + } + return false; + } + break; + + case "add_user_public_data": + let supernode_flo_public_key = localbitcoinplusplus.wallets.my_local_flo_public_key; + RM_RPC.filter_legit_requests(res_obj.params[0].trader_flo_address, + function (is_valid_request) { + if (is_valid_request !== true) return false; + + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let req_data = res_obj.params[0]; + try { + let flo_address = bitjs.FLO_TEST.pubkey2address(req_data.trader_flo_pubKey); + + if (flo_address == req_data.trader_flo_address && req_data.trader_flo_address + .length > 0) { + + let public_req_object = { + trader_flo_address: req_data.trader_flo_address, + trader_flo_pubKey: req_data.trader_flo_pubKey, + supernode_flo_public_key: supernode_flo_public_key, + trader_reputation: 0, + timestamp: +new Date() + } + + 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; + 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_addDB('userPublicData', public_req_object); + }); + + } + } catch (error) { + throw new Error('Invalid public key and flo address combination.'); + } + } + }); + break; + + case "superNodeSignedAddUserPublicData": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + doSend(JSON.stringify(response_from_sever)); // send response to client + break; + + case "refresh_deposit_status_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; + + localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.params[0].trader_flo_address) + .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_readDBbyIndex("deposit", 'status', 1).then(function (res) { + res.map(async function (deposit_trade) { + if (localbitcoinplusplus.master_configurations.tradableAsset1 + .includes(deposit_trade.product)) { + validateDepositedBTCBalance(deposit_trade, primarySupernodeOfThisUser); + } + }); + }); + }); + }); + break; + + case "update_external_file_request": + RM_RPC.filter_legit_requests(res_obj.params[0].trader_flo_address, is_valid_request => { + if (is_valid_request !== true) return false; + let update_script_request = res_obj.params[0]; + + if (typeof update_script_request.trader_flo_address !== "string") throw new Error( + "Unknown user"); + + let server_pubkey = localbitcoinplusplus.wallets.my_local_flo_public_key; + + 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; + 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 (typeof update_script_request.file_to_update == "string") { + + backup_server_db_instance.backup_readDB("external_files", update_script_request.file_to_update).then( + file_details => { + if (typeof file_details !== "undefined" + && typeof file_details.content == "string" && file_details + .content.length > 0) { + let file_details_string = JSON.stringify(file_details); + let server_sign = RM_WALLET + .sign(file_details_string, localbitcoinplusplus.wallets + .MY_SUPERNODE_PRIVATE_KEY); + RM_RPC.send_rpc + .call(this, "update_external_file_server_response", { + trader_flo_address: update_script_request.trader_flo_address, + file_updated: file_details, + server_sign: server_sign, + server_pubkey: server_pubkey, + filename: update_script_request.file_to_update, + receiver_flo_address: update_script_request.trader_flo_address + }).then(response_from_sever=>doSend(response_from_sever)); + } + }); + } else { + backup_server_db_instance.backup_readAllDB("external_files").then(file_details => { + if (file_details.length > 0) { + let file_details_str = JSON.stringify(file_details); + let server_sign = RM_WALLET + .sign(file_details_str, localbitcoinplusplus.wallets + .MY_SUPERNODE_PRIVATE_KEY); + RM_RPC.send_rpc + .call(this, "update_external_file_server_response", { + trader_flo_address: update_script_request.trader_flo_address, + file_updated: file_details, + server_sign: server_sign, + server_pubkey: server_pubkey, + filename: "UPDATE_ALL_FILES", + receiver_flo_address: update_script_request.trader_flo_address, + }).then(response_from_sever=>doSend(response_from_sever)); + } + }); + } + }); + + }); + break; + + case "update_external_file_server_response": + response_from_sever = RM_RPC.backup_receive_rpc_response.call(this, + JSON.stringify(res_obj)); + doSend(JSON.stringify(response_from_sever)); // send response to client + break; + + case "updateUserCryptoBalanceRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + let updateUserCryptoBalanceResponseObject = res_obj.params[0]; + let updateUserCryptoBalanceResponseString = JSON.stringify( + updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject); + let updateUserCryptoBalanceResponseStringHash = Crypto.SHA256(updateUserCryptoBalanceResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserCryptoBalanceResponseStringHash, + updateUserCryptoBalanceResponseObject.updatedBTCBalanceObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + updateinDB("crypto_balances", updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject, + updateUserCryptoBalanceResponseObject.trader_flo_address); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserCryptoBalanceResponseObject.trader_flo_address) { + displayBalances(updateUserCryptoBalanceResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); + } + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + } + + break; + + case "updateUsertraderDepositsRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + let updateUserDepositsResponseObject = res_obj.params[0]; + let updateUserDepositsResponseString = JSON.stringify( + updateUserDepositsResponseObject.updatedTraderDepositObject); + let updateUserDepositsResponseStringHash = Crypto.SHA256(updateUserDepositsResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserDepositsResponseStringHash, + updateUserDepositsResponseObject.updatedDepositsObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + updateinDB("deposit", updateUserDepositsResponseObject.updatedTraderDepositObject); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserDepositsResponseObject.trader_flo_address) { + displayBalances(updateUserDepositsResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); + } + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + } + break; + + case "addNewKbucketNode": + if (!localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return; + try { + let mss = ''; + let tid = res_obj.params[0].trader_flo_address || res_obj.globalParams.senderFloId; + localbitcoinplusplus.kademlia.determineClosestSupernode(tid) + .then(async my_closest_su=>{ + + if (my_closest_su[0].data.id !== localbitcoinplusplus.wallets.my_local_flo_address) return; + + const newKbucketObjectObj = res_obj.params[0]; + const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; + const getPubKeyOfSupernodeOfThisUser = RM_WALLET.getSupernodePublicKeyFromFloId(primarySupernodeOfThisUser); + const primaryKBOfTheUser = `SKBucket_${getPubKeyOfSupernodeOfThisUser}`; + if (typeof primaryKBOfTheUser !=="object") { + let mss = `ERROR: No such KBucket exists: ${primaryKBOfTheUser}` + showMessage(mss); + throw new Error(mss); + } + if (typeof newKbucketObjectObj.newKbucketNode == "object") { + newKbucketObject = newKbucketObjectObj.newKbucketNode; + + newKbucketObject_id_array = Object.values(newKbucketObject.id); + newKbucketObject_idu8 = new Uint8Array(newKbucketObject_id_array); + + localbitcoinplusplus.kademlia.addNewUserNodeInKbucket("FLO_TEST", + newKbucketObject_idu8, newKbucketObject.data, primaryKBOfTheUser); + + localbitcoinplusplus.rpc.prototype + .send_rpc + .call(this, "requestSupernodesToRemoveAUserFloIdFromTheirKBucket", { + redundantKbucketNodeU8Id: newKbucketObject_idu8, + currentSupernodeFloId: localbitcoinplusplus.wallets.my_local_flo_address, + trader_flo_address: res_obj.globalParams.senderFloId + }).then(removeRedundantKNode=>doSend(removeRedundantKNode)); + + } else { + mss = `WARNING: Failed to add ${res_obj.globalParams.senderFloId} to KBucket.`; + showMessage(mss) + console.warn(mss); + } + }); + + } catch (error) { + console.error(error); + } + break; + + case "queryKbucket": + try { + const kBucketQuery = res_obj.params[0]; + const kfrom = kBucketQuery.query.from; + const kto = kBucketQuery.query.to; + const kmsg = kBucketQuery.query.msg; + + buckId = localbitcoinplusplus.kademlia.floIdToKbucketId("FLO_TEST", kto); + const getItem = KBucket.get(buckId); + const getData = getItem.data; + + } catch (error) { + console.error(error); + } + break; + + case "link_My_Local_IP_To_My_Flo_Id": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const req_params = res_obj.params[0]; + if(typeof req_params.requesters_pub_key !== "string") return; + let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); + if(typeof flo_addr_for_pubkey !== "string") return; + if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; + updateinDB('ipTable', { + 'flo_public_key': req_params.requesters_pub_key, + 'temporary_ip': incoming_msg_local_ip + }).then((ipRes)=>{ + reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); + }).finally(()=>{ + linkBackOthersLocalIPToTheirFloId(); + }); + } + break; + + case "link_Others_Local_IP_To_Their_Flo_Id": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const req_params = res_obj.params[0]; + if(typeof req_params.requesters_pub_key !== "string") return; + let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); + if(typeof flo_addr_for_pubkey !== "string") return; + if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; + updateinDB('ipTable', { + 'flo_public_key': req_params.requesters_pub_key, + 'temporary_ip': incoming_msg_local_ip + }).then((ipRes)=>{ + reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); + }); + } + break; + + case "supernode_to_supernode_backup_request": + + // RM_RPC.filter_legit_requests(function (is_valid_request) { + // if (is_valid_request === true) { + let data = res_obj.params[0]; + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", + "crypto_balances", "cash_balances", "userPublicData", + "buyOrders", "sellOrders" + ]; + localbitcoinplusplus.actions.get_sharable_db_data(tableArray) + .then(function (su_db_data) { + su_db_data.trader_flo_address = data.trader_flo_address; + + let msg_sha256 = Crypto.SHA256(JSON.stringify(su_db_data)); + + localbitcoinplusplus.encrypt + .messageBroadcasting(msg_sha256, data.trader_flo_address, + "supernode_to_supernode_backup_response"); + + // if (typeof su_db_data == "object") { + // su_db_data.trader_flo_address = data.trader_flo_address; + // let server_sync_response = RM_RPC + // .send_rpc + // .call(this, "supernode_to_supernode_backup_response", + // su_db_data); + // doSend(server_sync_response); + // } + }) + // } + // }) + break; + + case "supernode_to_supernode_backup_response": + console.log(res_obj.params[0]); + + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let su_db_data = res_obj.params[0]; + + let db_data = localbitcoinplusplus.encrypt.decryptMessage(su_db_data.secret, su_db_data.senderPublicKeyString); + console.log(db_data); + return; + + 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 () { + for (let tableStoreName in su_db_data) { + // skip loop if the property is from prototype + if (tableStoreName == 'trader_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 BACKUP_DB.backup_updateinDB(tableStoreName, obj[prop], obj[ + prop].trader_flo_address); + } + } + } else { + if (resdbdata !== false) { + if (obj.length > 0) { + for (var prop in obj) { + if (!obj.hasOwnProperty(prop)) continue; + await BACKUP_DB.backup_updateinDB(resdbdata, obj[prop], + obj[prop].trader_flo_address, true, false); + } + } + } + } + + } catch (error) { + console.log(error); + } + } + })(); + } + break; + + case "messageBroadcasting": + console.log(res_obj); + try { + let response = res_obj.params[0]; + let msg = localbitcoinplusplus.encrypt.decryptMessage(response.data.secret, response.data.senderPublicKeyString); + console.log(msg); + } catch (error) { + console.error(error); + } + break; + + case "MessageForMiddleman": + RM_RPC.filter_legit_requests(dataToBeSentToReceiver.sender_flo_address, + function (is_valid_request) { + console.log(is_valid_request); + } + ); + break; + + case "backup_server_sync_response": + console.log(res_obj); + break; + + case "sync_data_by_vector_clock": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + try { + + (async function() { + let req_dt = res_obj.params[0]; + let db_instance = localbitcoinplusplus.newBackupDatabase.db[req_dt.leaving_supernode_flo_id]; + let dbTable = req_dt.dbTable; + let data = req_dt.data; + let subjectUser = data.trader_flo_address; + let mss = ''; + + if (typeof data.id !== "string" && typeof data.id !== "number") { + mss = `WARNING: Failed to sync data by vector clock as id field could not be found.`; + showMessage(mss); + throw new Error(mss); + } + if (typeof db_instance !=="object") { + mss = `WARNING: Failed to sync data by vector clock as invalid DB instance was encountered.`; + showMessage(mss); + throw new Error(mss); + } + let myOwnDBData = await db_instance.backup_readDB(dbTable, data.id); + if (typeof myOwnDBData.vectorClock !== "number") { + mss = `WARNING: Failed to sync data by vector clock as id field could not be found.`; + showMessage(mss); + throw new Error(mss); + } + if (data.vectorClock < myOwnDBData.vectorClock) { + // You have the latest data, send it to other supernodes + + let getNextClosestSuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode("", 3, supernodeKBucket, req_dt.leaving_supernode_flo_id); + let nextBackupSupernode = getNextClosestSuObj[1].data.id; + + if (typeof nextBackupSupernode !== "string") { + let msg = `WARNING: Failed to determine next closest backup supernode for ${req_dt.leaving_supernode_flo_id}.`; + showMessage(msg); + throw new Error(msg); + } + + getNextClosestSuObj.map((nextSu, i)=>{ + if(nextSu.data.id !==localbitcoinplusplus.wallets.my_local_flo_address) { + let nextSuConn = localbitcoinplusplus.backupWS[nextSu.data.id]; + if(typeof nextSuConn !== "object") { + let msg = `WARNING: Failed to open a backup WS connection with Supernode ${nextSu}.`; + showMessage(msg); + throw new Error(msg); + } + RM_RPC + .send_rpc + .call(this, "sync_data_by_vector_clock", { + trader_flo_address: data.trader_flo_address, + receiver_flo_address: nextSu.data.id, + leaving_supernode_flo_id: req_dt.leaving_supernode_flo_id, + data: myOwnDBData, + dbTable: dbTable + }).then(server_response=> + doSend(server_response, nextSu.data.id)); + + } + + }) + + } else if(data.vectorClock > myOwnDBData.vectorClock) { + // You have old data, update respective DB. + db_instance.backup_updateinDB(dbTable, data).then(()=>{ + showMessage(`INFO: Data updated in ${dbTable} for id ${data.id}.`); + }); + } + + })() + } catch (error) { + throw new Error(error); + } + + } + } + break; + + case "nextBackupSupernodeToSyncDataBeforeActingAsBackupSupernodeNodeRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const userFLoID = res_obj.params[0].trader_flo_address; + if (typeof userFLoID !== "string" || userFLoID.length<1) { + console.warn(`Invalid FLO Id`); + return; + } + (async function() { + + 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)) { + /********************************************************************* + *DISABLE ANY FURTHER REQUEST BY USER HERE UNTIL FULL SYNC IS COMPLETE* + **********************************************************************/ + + getSupernodeClosestSuObj.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', userFLoID); + + record.map(rec=>{ + RM_RPC + .send_rpc + .call(this, "sync_data_by_vector_clock", { + trader_flo_address: userFLoID, + receiver_flo_address: nextSu.data.id, + leaving_supernode_flo_id: leaving_supernode_flo_id, + data: rec, + dbTable: tbl + }).then(server_response=> + doSend(server_response, nextSu.data.id)); + + }); + + }); + + } + }); + } + + }); + })(); + + } + } + break; + + case "is_node_alive_request": + if(localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + reactor.dispatchEvent('nodeIsAlive', res_obj); + // Send your live status to Sender + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "yup_i_am_awake", { + JOB: 'I_AM_ALIVE', + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address, + receiver_flo_address: res_obj.globalParams.senderFloId + }).then(req=>doSend(req)); + } + break; + + case "yup_i_am_awake": + if (res_obj.method=="yup_i_am_awake" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && (res_obj.params[0].receiver_flo_address == + localbitcoinplusplus.wallets.my_local_flo_address)) { + + reactor.dispatchEvent('nodeIsAlive', res_obj); + return; + } + break; + + case "reconnect_with_another_supernode": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + ) { + (async function() { + let su_db_data = res_obj.params[0]; + const MCS = await localbitcoinplusplus.kademlia + .determineClosestSupernode(localbitcoinplusplus.wallets.my_local_flo_address, 10); + + // If user is already connected to primary return back + if (localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS == MCS[0].data.id) return; + + // If user is already connected to given Supernode return + if (localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS == su_db_data.trader_flo_address) return; + + const mcslist = MCS.map(m=>m.data.id); + + if(mcslist.indexOf(su_db_data.trader_flo_address) < mcslist.indexOf(localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS)) { + if (websocket.readyState === WebSocket.OPEN) { + websocket.close(); + const newSu = await readDBbyIndex('myClosestSupernodes','trader_flo_address', su_db_data.trader_flo_address); + if (typeof newSu=="object" && typeof newSu[0].trader_flo_address=="string") { + await startWebSocket(`ws://${newSu[0].ip}:${newSu[0].port}`); + showMessage(`INFO: ${su_db_data.server_msg}`); + } else { + alert(`INFO: Please reload the page.`); + } + } + } + + })(); + } + break; + + case "update_deposited_crypto_instance": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const res_data = res_obj.params[0]; + const backup_db = res_data.db_inst; + let _updateinDB = updateinDB; + try { + if (typeof backup_db=="string" && backup_db.length>0) { + if (localbitcoinplusplus.wallets.my_local_flo_address!==backup_db + && typeof localbitcoinplusplus.newBackupDatabase.db[backup_db] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[backup_db]; + _updateinDB = foreign_db.backup_updateinDB.bind(foreign_db); + } + + if (typeof res_data.deposit_data == "object") + _updateinDB('deposit', res_data.deposit_data, res_data.deposit_data.id, true, false); + if (typeof res_data.btc_reserve_data == "object") + _updateinDB('system_btc_reserves_private_keys', res_data.btc_reserve_data, + res_data.btc_reserve_data.id.id, true, false); + + } + } catch (e) { + console.error(e); + } + } + break; + + case "delete_deposited_crypto_instance": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const res_data = res_obj.params[0]; + const backup_db = res_data.db_inst; + let _removeinDB = removeinDB; + try { + if (typeof backup_db=="string" && backup_db.length>0) { + if (localbitcoinplusplus.wallets.my_local_flo_address!==backup_db + && typeof localbitcoinplusplus.newBackupDatabase.db[backup_db] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[backup_db]; + _removeinDB = foreign_db.backup_removeinDB.bind(foreign_db); + } + if (typeof res_data.withdraw_btc_id == "string") _removeinDB('withdraw_btc', res_data.withdraw_btc_id); + if (typeof res_data.deposit_id == "string") _removeinDB('deposit', res_data.deposit_id); + if (typeof res_data.btc_reserve_id == "string") _removeinDB('system_btc_reserves_private_keys', res_data.btc_reserve_id); + } + } catch (e) { + console.error(e); + } + } + break; + + case "request_me_db_data": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const resp = res_obj.params[0]; + + if(localbitcoinplusplus.wallets.my_local_flo_address !== resp.receiver_flo_address) return; + + localbitcoinplusplus.actions + .sync_backup_supernode_from_backup_supernode( + localbitcoinplusplus.wallets.my_local_flo_address, + resp.trader_flo_address, + resp.db_inst + ); + } + break; + + default: + break; + } + } + } catch (error) { + console.error(error); + return; + } + } + } + + async function handle_backup_server_messages(response) { + + //var response = evt.data; + console.log('backup response: '+response); + + let isItANodeLeavingMessage = response.search(`\\-- left`); + + if(isItANodeLeavingMessage >= 0) { + reactor.dispatchEvent('fireNodeGoodByeEvent', response); + } + + var res_pos = response.indexOf('{'); + if (res_pos >= 0) { + let isRequestToLinkIp = response.search("linkMyLocalIPToMyFloId"); + let isRequestToLinkOthersIp = response.search("link_Others_Local_IP_To_Their_Flo_Id"); + let incoming_msg_local_ip = ``; + if (isRequestToLinkIp>=0 || isRequestToLinkOthersIp>=0) { + let index_of_ip = response.indexOf(' '); + if (incoming_msg_local_ip>=0) { + incoming_msg_local_ip = response.substr(0, index_of_ip); + } + } + var res = response.substr(res_pos); + try { + + const isIncomingMessageValid = await validateIncomingMessage(res); + console.log("isIncomingMessageValid (Backup): ", isIncomingMessageValid); + + var res_obj = JSON.parse(res); + + // Only supernodes are allowed to go below + if (!localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) return; + + if (typeof res_obj.globalParams.receiverFloId=="string" + && res_obj.globalParams.receiverFloId !== + localbitcoinplusplus.wallets.my_local_flo_address) return; + + if (typeof res_obj.method !== "undefined") { + let response_from_sever; + + const RM_WALLET = new localbitcoinplusplus.wallets; + const RM_TRADE = new localbitcoinplusplus.trade; + const RM_RPC = new localbitcoinplusplus.rpc; + + switch (res_obj.method) { + case "give_me_your_backup": + if (typeof res_obj.params == "object" + && typeof res_obj.params[0] == "object") { + let received_resp = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(received_resp.trader_flo_address, + function (is_valid_request) { + if(!is_valid_request || received_resp.JOB!=="BACKUP_SERVER_REQUEST") return; + const requester_supernode_pubkey = received_resp.requesters_pub_key; + const requester_supernode_flo_address = received_resp.trader_flo_address; + + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", + "crypto_balances", "cash_balances", "userPublicData", + "buyOrders", "sellOrders" + ]; + localbitcoinplusplus.actions.get_sharable_db_data(tableArray).then( + function (su_db_data) { + if (typeof su_db_data == "object") { + su_db_data.trader_flo_address = localbitcoinplusplus.wallets.my_local_flo_address; + su_db_data.receiver_flo_address = requester_supernode_flo_address; + RM_RPC + .send_rpc + .call(this, "backup_server_sync_response", + su_db_data) + .then(server_sync_response=>doSend(server_sync_response)); + } + }); + + }); + } + break; + + case "server_sync_response": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object" + || localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return; + let su_backup_db_data = res_obj.params[0]; + + if(localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) return; + + RM_RPC.filter_legit_backup_requests(su_backup_db_data.trader_flo_address, + async function (is_valid_request) { + if(!is_valid_request) return false; + + (async function () { + for (let tableStoreName in su_backup_db_data) { + // skip loop if the property is from prototype + if (tableStoreName == 'trader_flo_address' || !su_backup_db_data.hasOwnProperty( + tableStoreName)) continue; + + try { + let obj = su_backup_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); + } + } + } 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]); + } + } + } + } + + } catch (error) { + console.log(error); + } + } + })(); + + }); + + break; + + case "trade_buy_request_response": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let buyOrders_data = res_obj.params[0]; + + if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && + localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + buyOrders_data.supernodePubKey)) { + let isDataSignedBySuperNode = RM_WALLET + .verify(buyOrders_data.data_hash, buyOrders_data.supernode_sign, + buyOrders_data.supernodePubKey); + if (isDataSignedBySuperNode === true) { + + let getPrimarySuObj = await localbitcoinplusplus.kademlia.determineClosestSupernode(buyOrders_data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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); + }; + + // Add buy order + backup_server_db_instance.backup_addDB("buyOrders", buyOrders_data).then(() => { + showMessage(`Your buy order is placed successfully.`); + }); + } + } + break; + case "trade_sell_request_response": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let sellOrders_data = res_obj.params[0]; + if (typeof localbitcoinplusplus.master_configurations.supernodesPubKeys == "object" && + localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(sellOrders_data.supernodePubKey)) { + let isDataSignedBySuperNode = RM_WALLET + .verify(sellOrders_data.data_hash, sellOrders_data.supernode_sign, + sellOrders_data.supernodePubKey); + if (isDataSignedBySuperNode === true) { + let getPrimarySuObj = await localbitcoinplusplus.kademlia.determineClosestSupernode(sellOrders_data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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); + }; + + // Add buy order + backup_server_db_instance.backup_addDB("sellOrders", sellOrders_data).then(() => { + showMessage(`Your sell order is placed successfully.`); + }); + } + } + } + break; + + case "deposit_asset_request_response": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let deposit_res_data = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(deposit_res_data.trader_flo_address, + async function (is_valid_request) { + if(!is_valid_request) return false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" + && typeof res_obj.params[0].data == "object") { + let resp = res_obj.params[0]; + if (RM_WALLET + .verify(resp.data.depositDataHash, resp.data.order_validator_sign, resp.data.order_validator_public_key) + ) { + let getPrimarySuObj = await localbitcoinplusplus.kademlia.determineClosestSupernode(resp.data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + 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_addDB('deposit', resp.data); + if (typeof resp.withdrawer_data == "object") { + backup_server_db_instance.backup_updateinDB("withdraw_cash", resp.withdrawer_data, resp.withdrawer_data.trader_flo_address); + } + backup_server_db_instance.backup_readDB("localbitcoinUser", "00-01").then(function (user) { + if (typeof user == "object" && user.myLocalFLOAddress == resp.data.trader_flo_address) { + let counterTraderAccountAddress = + `Please pay the amount to following address:
+${resp.msg}
`; + showMessage(counterTraderAccountAddress); + modalWindow(counterTraderAccountAddress); + } + }); + } + } + }); + break; + + case "withdrawal_request_response": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let withdrawal_res_data = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(withdrawal_res_data.trader_flo_address, + async function (is_valid_request) { + if(!is_valid_request) return false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + if (RM_WALLET + .verify(res_obj.params[0].withdrawDataHash, res_obj.params[0].order_validator_sign, + res_obj.params[0].order_validator_public_key)) { + + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(withdrawal_res_data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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_addDB('withdraw_cash', res_obj.params[0]).then(() => { + showMessage(`Your cash withdrawal request is placed successfully.`); + }); + } + } + }); + break; + + case "cancel_trade": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let cancel_res_data = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(cancel_res_data.trader_flo_address, + async function (is_valid_request) { + if(!is_valid_request) return false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let cancel_request = res_obj.params[0]; + if (cancel_request.job == "cancel_trade_request") { + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(cancel_res_data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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("userPublicData", cancel_request.trader_flo_address).then((trader_data) => { + if (typeof trader_data.trader_flo_address !== "string" || typeof trader_data + .trader_flo_pubKey !== "string") { + err_msg="ERROR: Failed to cancel the trade. User is unknown."; + showMessage(err_msg); + throw new Error(err_msg); + } + tradeDB = cancel_request.trade_type == "buy" ? "buyOrders" : + "sellOrders"; + if (RM_WALLET + .verify(cancel_request.trade_id, cancel_request.signed_trade_id, + trader_data.trader_flo_pubKey)) { + backup_server_db_instance.backup_removeinDB(tradeDB, cancel_request.trade_id) + .then((id) => showMessage(`Trade Id ${id} deleted.`)); + } else { + showMessage( + `Failed to verify trade for trade id ${cancel_request.trade_id}` + ); + } + }) + } else { + showMessage("Failed to cancel trade."); + } + } + }); + break; + + case "trade_balance_updates": + + if(!localbitcoinplusplus.master_configurations.supernodesPubKeys.includes(res_obj.nodePubKey)) return; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const trade_balance_res = res_obj.params[0]; + // Verify data + let trade_info_str = JSON.stringify(trade_balance_res.trade_infos); + let buyer_cash_data_str = JSON.stringify(trade_balance_res.buyer_cash_data); + let seller_cash_data_str = JSON.stringify(trade_balance_res.seller_cash_data); + let buyer_btc_data_str = JSON.stringify(trade_balance_res.buyer_btc_data); + let seller_btc_data_str = JSON.stringify(trade_balance_res.seller_btc_data); + + let res_str = + `${trade_info_str}${buyer_cash_data_str}${seller_cash_data_str}${buyer_btc_data_str}${seller_btc_data_str}`; + let hashed_data = Crypto.SHA256(res_str); + + RM_RPC.filter_legit_backup_requests(trade_balance_res.trade_infos.buyer_flo_id, + async function (is_valid_request) { + if (is_valid_request !== true) return false; + + if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( + trade_balance_res.supernodePubKey)) { + if (RM_WALLET.verify(hashed_data, + trade_balance_res.supernode_sign, trade_balance_res.supernodePubKey)) { + + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(trade_balance_res.trade_infos.buyer_flo_id); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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); + }; + + // Delete orders in clients DB + try { + backup_server_db_instance.backup_removeinDB("buyOrders", trade_balance_res.trade_infos.buy_order_id); + backup_server_db_instance.backup_removeinDB("sellOrders", trade_balance_res.trade_infos.sell_order_id); + } catch (error) { + callback(false); + throw new Error(error); + } + + // Update balances in clients DB + try { + backup_server_db_instance.backup_updateinDB("cash_balances", trade_balance_res.buyer_cash_data, + trade_balance_res.trade_infos.buyer_flo_id, true, false); + backup_server_db_instance.backup_updateinDB("cash_balances", trade_balance_res.seller_cash_data, + trade_balance_res.trade_infos.seller_flo_id, true, false); + backup_server_db_instance.backup_updateinDB("crypto_balances", trade_balance_res.buyer_btc_data, + trade_balance_res.trade_infos.buyer_flo_id, true, false); + backup_server_db_instance.backup_updateinDB("crypto_balances", trade_balance_res.seller_btc_data, + trade_balance_res.trade_infos.seller_flo_id, true, false); + + // Update balances + displayBalances(localbitcoinplusplus.wallets.my_local_flo_address); + } catch (error) { + callback(false); + throw new Error(error); + } + } + } + }); + } + break; + + case "update_all_withdraw_cash_depositor_claim": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let withdraw_caim_res_data = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(withdraw_caim_res_data.trader_flo_address, + async function (is_valid_request) { + if(!is_valid_request) return false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let depositor_claim_response_object = res_obj.params[0]; + let update_withdraw_cash_obj_data_res = { + depositor_claim: depositor_claim_response_object.depositor_claim + }; + let update_withdraw_cash_obj_data_res_str = JSON.stringify( + update_withdraw_cash_obj_data_res); + let depositor_claim_response_data_hash = Crypto.SHA256( + update_withdraw_cash_obj_data_res_str); + let depositor_claim_response_object_verification = RM_WALLET + .verify(depositor_claim_response_data_hash, depositor_claim_response_object.sign, + depositor_claim_response_object.publicKey); + + if ((depositor_claim_response_data_hash == depositor_claim_response_object.hash) && + (depositor_claim_response_object_verification == true)) { + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(withdraw_caim_res_data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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_updateinDB('withdraw_cash', depositor_claim_response_object.depositor_claim, + depositor_claim_response_object.depositor_claim.id); + return true; + } + return false; + } + }); + break; + + case "update_all_deposit_withdraw_success": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let update_deposit_withdraw_claim_data = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(update_deposit_withdraw_claim_data.trader_flo_address, + async function (is_valid_request) { + if(!is_valid_request) return false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let withdraw_success_response = res_obj.params[0]; + let update_cash_balance_obj_res = { + depositor_cash_data: withdraw_success_response.depositor_cash_data, + withdrawer_cash_data: withdraw_success_response.withdrawer_cash_data + } + let update_cash_balance_obj_res_str = JSON.stringify(update_cash_balance_obj_res); + let update_cash_balance_obj_res_hash = Crypto.SHA256( + update_cash_balance_obj_res_str); + let update_cash_balance_obj_res_verification = RM_WALLET + .verify(update_cash_balance_obj_res_hash, withdraw_success_response.sign, + withdraw_success_response.publicKey); + + if ((update_cash_balance_obj_res_hash == withdraw_success_response.hash) && + update_cash_balance_obj_res_verification == true) { + let getPrimarySuObj = await localbitcoinplusplus.kademlia + .determineClosestSupernode(update_deposit_withdraw_claim_data.trader_flo_address); + const primarySupernode = getPrimarySuObj[0].data.id; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernode]; + + 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_updateinDB('cash_balances', withdraw_success_response.depositor_cash_data); + backup_server_db_instance.backup_updateinDB('cash_balances', withdraw_success_response.withdrawer_cash_data); + backup_server_db_instance.backup_removeByIndex('deposit', 'trader_flo_address', withdraw_success_response.depositor_cash_data + .trader_flo_address); + backup_server_db_instance.backup_removeinDB('withdraw_cash', withdraw_success_response.withdraw_id); + + // Update balances + displayBalances(localbitcoinplusplus.wallets.my_local_flo_address); + + return true; + } + return false; + } + }); + break; + case "requestSupernodesToRemoveAUserFloIdFromTheirKBucket": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let removeUserFromKBData = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(removeUserFromKBData.trader_flo_address, + function (is_valid_request) { + if(!is_valid_request) return false; + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const removeKBReq = res_obj.params[0]; + const currentSupernodeFloIdOfUser = removeKBReq.currentSupernodeFloId; + if(localbitcoinplusplus.wallets.my_local_flo_address == currentSupernodeFloIdOfUser) return; + const userKbucketObject_id_array = Object.values(removeKBReq.redundantKbucketNodeU8Id); + const userKBId = new Uint8Array(userKbucketObject_id_array); + KBucket.remove(userKBId); + return true; + } + }); + break; + case "requestSupernodesKBucketData": + if (typeof res_obj.params !== "object" + || typeof res_obj.params[0] !== "object") return; + let reqSuKBData = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(reqSuKBData.trader_flo_address, + function (is_valid_request) { + if(!is_valid_request) return false; + if(typeof res_obj.globalParams.senderFloId !=="string") return; + let sender = res_obj.globalParams.senderFloId; + readAllDB('kBucketStore') + .then(myKBData=>{ + myKBData.receiver_flo_address = sender; + + localbitcoinplusplus.rpc.prototype + .send_rpc + .call(this, "SupernodesKBucketDataResponse", myKBData) + .then(sendBackMySupernodeKBucket=> + doSend(sendBackMySupernodeKBucket)); + }) + } + ); + break; + case "SupernodesKBucketDataResponse": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const reqSuKBResponseData = res_obj.params[0]; + RM_RPC.filter_legit_backup_requests(reqSuKBResponseData.trader_flo_address, + function (is_valid_request) { + if(!is_valid_request) return false; + reqSuKBResponseData.map(kd=> { + let kb = window[`SKBucket_${kd.primary_supernode_flo_public_key}`]; + localbitcoinplusplus.kademlia.addFullKBDataInKBucketAndDB(kd, kb)}) + }); + } + break; + case "link_My_Local_IP_To_My_Flo_Id": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const req_params = res_obj.params[0]; + if(typeof req_params.requesters_pub_key !== "string") return; + let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); + if(typeof flo_addr_for_pubkey !== "string") return; + if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; + updateinDB('ipTable', { + 'flo_public_key': req_params.requesters_pub_key, + 'temporary_ip': incoming_msg_local_ip + }).then((ipRes)=>{ + reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); + }).finally(()=>{ + linkBackOthersLocalIPToTheirFloId(); + }); + } + break; + case "link_Others_Local_IP_To_Their_Flo_Id": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + const req_params = res_obj.params[0]; + if(typeof req_params.requesters_pub_key !== "string") return; + let flo_addr_for_pubkey = bitjs.FLO_TEST.pubkey2address(req_params.requesters_pub_key); + if(typeof flo_addr_for_pubkey !== "string") return; + if(flo_addr_for_pubkey !== res_obj.globalParams.senderFloId) return; + updateinDB('ipTable', { + 'flo_public_key': req_params.requesters_pub_key, + 'temporary_ip': incoming_msg_local_ip + }).then((ipRes)=>{ + reactor.dispatchEvent('fireNodeWelcomeBackEvent', ipRes); + }); + } + break; + + case "add_user_public_data": + let supernode_flo_public_key = localbitcoinplusplus.wallets.my_local_flo_public_key; + 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 == "object" && typeof res_obj.params[0] == "object") { + let req_data = res_obj.params[0]; + try { + let flo_address = bitjs.FLO_TEST.pubkey2address(req_data.trader_flo_pubKey); + + if (flo_address == req_data.trader_flo_address && req_data.trader_flo_address + .length > 0) { + + let public_req_object = { + trader_flo_address: req_data.trader_flo_address, + trader_flo_pubKey: req_data.trader_flo_pubKey, + supernode_flo_public_key: supernode_flo_public_key, + trader_reputation: 0, + timestamp: +new Date() + } + + addDB('userPublicData', public_req_object); + + localbitcoinplusplus.kademlia.determineClosestSupernode(req_data.trader_flo_address) + .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_addDB('userPublicData', public_req_object); + }); + + } + } catch (error) { + throw new Error('Invalid public key and flo address combination.'); + } + } + }); + break; + + case "add_user_public_data_inBackup_db": + RM_RPC.filter_legit_backup_requests(res_obj.params[0].trader_flo_address, + function (is_valid_request) { + if (is_valid_request !== true) return false; + + let supernode_flo_public_key = localbitcoinplusplus.wallets.my_local_flo_public_key; + + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let req_data = res_obj.params[0]; + try { + let flo_address = bitjs.FLO_TEST.pubkey2address(req_data.trader_flo_pubKey); + + if (flo_address == req_data.trader_flo_address && req_data.trader_flo_address + .length > 0) { + + let public_req_object = { + trader_flo_address: req_data.trader_flo_address, + trader_flo_pubKey: req_data.trader_flo_pubKey, + supernode_flo_public_key: supernode_flo_public_key, + trader_reputation: 0, + timestamp: +new Date() + } + + addDB('userPublicData', public_req_object); + + localbitcoinplusplus.kademlia.determineClosestSupernode(req_data.trader_flo_address) + .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_addDB('userPublicData', public_req_object); + }); + + } + } catch (error) { + throw new Error('Invalid public key and flo address combination.'); + } + } + }); + break; + + case "send_back_shamirs_secret_btc_pvtkey": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + + if(typeof res_obj.globalParams.senderFloId !="string") return; + + readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val).then(function ( + res) { + RM_RPC + .send_rpc + .call(this, "retrieve_shamirs_secret_btc_pvtkey", { + retrieve_pvtkey_req_id: res_obj.params[0].retrieve_pvtkey_req_id, + private_key_chunk: res, + withdraw_id: res_obj.params[0].withdraw_id, + receiver_flo_address: res_obj.globalParams.senderFloId, + db_inst: res_obj.params[0].db_inst + }).then(send_pvtkey_req=>doSend(send_pvtkey_req)); + }); + } + break; + + case "store_shamirs_secret_pvtkey_shares": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + delete res_obj.params[0].trader_flo_address; + addDB("supernode_private_key_chunks", res_obj.params[0]); + } + break; + + case "retrieve_shamirs_secret_btc_pvtkey": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" && + typeof res_obj.params[0].private_key_chunk == "object" && + typeof res_obj.params[0].retrieve_pvtkey_req_id == "string" && + typeof res_obj.params[0].withdraw_id == "string" && + typeof localbitcoinplusplus.wallets.my_local_flo_address == "string" && + res_obj.params[0].receiver_flo_address === localbitcoinplusplus.wallets.my_local_flo_address) { + + // This message was for Primary Supernode and is meant to be run in onMessage() + if(res_obj.params[0].db_inst==localbitcoinplusplus.wallets.my_local_flo_address) return; + + let shamirs_shares_response = res_obj.params[0]; + let retrieve_pvtkey_req_id = res_obj.params[0].retrieve_pvtkey_req_id; + let withdraw_id = res_obj.params[0].withdraw_id; + if (typeof btc_pvt_arr !== "object") btc_pvt_arr = []; + if (typeof btc_pvt_arr[retrieve_pvtkey_req_id] == "undefined") btc_pvt_arr[ + retrieve_pvtkey_req_id] = []; + + // Filter function below logic source - + // https://stackoverflow.com/a/9229821/5348972 + let seen_chunk_id_list = []; + + btc_pvt_arr[retrieve_pvtkey_req_id].filter(function(item) { + return seen_chunk_id_list.hasOwnProperty(item.private_key_chunk.id) + ? false : (seen_chunk_id_list.push(item.private_key_chunk.id)); + }); + + if (!seen_chunk_id_list + .includes(shamirs_shares_response.private_key_chunk.id) + && typeof shamirs_shares_response.private_key_chunk.id == "string") { + btc_pvt_arr[retrieve_pvtkey_req_id].push(shamirs_shares_response); + } + + if (btc_pvt_arr[retrieve_pvtkey_req_id].length === localbitcoinplusplus.master_configurations + .ShamirsMaxShares) { + delete res_obj.params[0].private_key_chunk; + res_obj.params[0].btc_private_key_array = JSON.stringify(btc_pvt_arr[ + retrieve_pvtkey_req_id]); + res_obj.params[0].trader_flo_address = localbitcoinplusplus.wallets.my_local_flo_address; + RM_RPC.backup_receive_rpc_response.call(this, JSON.stringify(res_obj)); + + localbitcoinplusplus.actions.delay(300000).then(()=>{ + btc_pvt_arr[retrieve_pvtkey_req_id] = []; // Unset the object + }); + } + } + break; + + case "updateUserCryptoBalanceRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + + let updateUserCryptoBalanceResponseObject = res_obj.params[0]; + + 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; + 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); + }; + + let updateUserCryptoBalanceResponseString = JSON.stringify( + updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject); + let updateUserCryptoBalanceResponseStringHash = Crypto.SHA256(updateUserCryptoBalanceResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserCryptoBalanceResponseStringHash, + updateUserCryptoBalanceResponseObject.updatedBTCBalanceObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + backup_server_db_instance.backup_updateinDB("crypto_balances", updateUserCryptoBalanceResponseObject.updatedBTCBalanceObject); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserCryptoBalanceResponseObject.trader_flo_address) { + displayBalances(updateUserCryptoBalanceResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); + } + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + }); + } + } + + break; + + case "updateUsertraderDepositsRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + let updateUserDepositsResponseObject = res_obj.params[0]; + + 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; + 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); + }; + + let updateUserDepositsResponseString = JSON.stringify( + updateUserDepositsResponseObject.updatedTraderDepositObject); + let updateUserDepositsResponseStringHash = Crypto.SHA256(updateUserDepositsResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserDepositsResponseStringHash, + updateUserDepositsResponseObject.updatedDepositsObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + backup_server_db_instance.backup_updateinDB("deposit", + updateUserDepositsResponseObject.updatedTraderDepositObject); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserDepositsResponseObject.trader_flo_address) { + displayBalances(updateUserDepositsResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); + } + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + }); + } + break; + + case "updateUserBTCReservesRequest": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + + let updateUserReservesResponseObject = res_obj.params[0];; + let subjectuser = res_obj.params[0].trader_flo_address || res_obj.params[0].updatedReservesObject[0].trader_flo_address; + let backup_server_db_instance = ""; + if(typeof subjectuser !="string") return; + localbitcoinplusplus.kademlia.determineClosestSupernode(subjectuser) + .then(my_closest_su_list=>{ + const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; + 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); + }; + + let updateUserReservesResponseString = JSON.stringify( + updateUserReservesResponseObject.updatedReservesObject); + let updateUserReservesResponseStringHash = Crypto.SHA256(updateUserReservesResponseString); + let isBalanceLegit = RM_WALLET.verify(updateUserReservesResponseStringHash, + updateUserReservesResponseObject.updatedBTCReservesObjectSign, + res_obj.nodePubKey + ); + if (isBalanceLegit) { + backup_server_db_instance.backup_updateinDB("system_btc_reserves_private_keys", + updateUserReservesResponseObject.updatedReservesObject[0], true, false); + if (localbitcoinplusplus.wallets.my_local_flo_address == + updateUserReservesResponseObject.trader_flo_address) { + displayBalances(updateUserReservesResponseObject.trader_flo_address); + showMessage(`INFO: Your balance is updated.`); + } + return true; + } else { + showMessage(`WARNING: Failed to update balance in your DB. Please refresh.`); + } + }); + } + break; + + case "sync_backup_supernode_from_backup_supernode_response": + let su_db_data = res_obj.params[0]; + if (typeof localbitcoinplusplus.wallets.my_local_flo_address !== "string" + || typeof su_db_data.receiver_flo_address !== "string" + || su_db_data.receiver_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address){ + console.warn(`WARNING: This backup response data was not meant for you.`); + return; + } + + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + + (async function () { + let _addDB = addDB; + let _removeAllinDB = removeAllinDB; + let _updateinDB = updateinDB; + let backup_db; + + if(typeof localbitcoinplusplus.wallets.my_local_flo_address !=="string" + || localbitcoinplusplus.wallets.my_local_flo_address.length<1) { + console.warn(`WARNING: Local FLO Address is not set.`); + return; + } + + if (su_db_data.trader_flo_address !==localbitcoinplusplus.wallets.my_local_flo_address) { + backup_db = su_db_data.trader_flo_address; + } + 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]; + _readDB = foreign_db.backup_readDB.bind(foreign_db); + _readDBbyIndex = foreign_db.backup_readDBbyIndex.bind(foreign_db); + _updateinDB = foreign_db.backup_updateinDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${backup_db}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } + let i = 0; + for (let tableStoreName in su_db_data) { + i++; + if (i==Object.keys(su_db_data).length-2) { + localbitcoinplusplus.services[`can_serve_${su_db_data.trader_flo_address}`] = true; + // Close unnecessary connections now + reactor.dispatchEvent('remove_extra_backup_connections'); + } + // 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 (obj.length > 0) { + for (var prop in obj) { + if (!obj.hasOwnProperty(prop)) continue; + _updateinDB(tableStoreName, obj[prop], obj[prop].id, true, false).then(()=>{ + showMessage(`INFO: "${tableStoreName}" datastore syncing is complete.`); + }); + } + } + + } catch (error) { + console.log(error); + } + } + })(); + } + break; + + case "do_you_have_latest_data_for_this_supernode": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const response_object = res_obj.params[0]; + if(typeof response_object.trader_flo_address !="string") return; + if(typeof response_object.allowed_receivers=="object" + && !response_object.allowed_receivers + .includes(localbitcoinplusplus.wallets.my_local_flo_address)) return; + + localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.params[0].trader_flo_address) + .then(async my_closest_su_list=>{ + const primarySupernodeOfThisUser = my_closest_su_list[0].data.id; + + let _readAllDB = readAllDB; + if (typeof primarySupernodeOfThisUser=="string" + && primarySupernodeOfThisUser.length>0 + && typeof primarySupernodeOfThisUser !== "undefined" + && primarySupernodeOfThisUser !== localbitcoinplusplus.wallets.my_local_flo_address + ) { + if (typeof localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser]; + _readAllDB = foreign_db.backup_readAllDB.bind(foreign_db); + } else { + err_msg = `WARNING: Invalid Backup DB Instance Id: ${primarySupernodeOfThisUser}.`; + showMessage(err_msg); + throw new Error(err_msg); + } + } + + // Inform (relevent) user nodes they should connect to another more eligible supenode + // Send only if you were the acting supernode for res_obj.globalParams.senderFloId + + RM_RPC + .send_rpc + .call(this, "reconnect_with_another_supernode", { + "trader_flo_address": res_obj.globalParams.senderFloId, + "server_msg": `A new Supernode is live and synced. You will now be connected to ${res_obj.globalParams.senderFloId}. + The process can take some time. You will be notified shortly once system is ready to serve.`, + }).then(server_response=>doSend(server_response)); + + const tableArray = ["deposit", "withdraw_cash", "withdraw_btc", "cash_balances", + "crypto_balances", "buyOrders", "sellOrders", "system_btc_reserves_private_keys"]; + + let backup_database = ""; + if (primarySupernodeOfThisUser!==localbitcoinplusplus.wallets.my_local_flo_address) { + backup_database = primarySupernodeOfThisUser; + } + + const su_db_data_from_my_db = await localbitcoinplusplus.actions.get_sharable_db_data(tableArray, backup_database); + + const dbHashData_from_my_db = await localbitcoinplusplus.actions.getDBTablesLatestHashAndTimestamp(primarySupernodeOfThisUser, su_db_data_from_my_db); + + // If you have same data as the sender has, you don't need to return any data to him + if (dbHashData_from_my_db.DBHash===response_object.DBHash) return; + if (dbHashData_from_my_db.id!==response_object.id) return; + if (dbHashData_from_my_db.data_of!==response_object.data_of) return; + + let mismatched_fields = []; + + for (var q in dbHashData_from_my_db) { + if (dbHashData_from_my_db.hasOwnProperty(q) + && q!='higestTimestampList' + && q!='id' + && q!='data_of' + && q!="DBHash" + && q!='trader_flo_address') { + if (dbHashData_from_my_db[q]!==response_object[q]) { + mismatched_fields.push(q); + } + } + } + + console.log(mismatched_fields); + + let latest_data = {}; + + for (var i = 0; i < mismatched_fields.length; i++) { + const mf = mismatched_fields[i]; + const res_data_obj = await _readAllDB(mf, false); + let filtered_data = res_data_obj.filter(odho=>{ + if (typeof odho.timestamp=="number" + && typeof response_object.higestTimestampList[`${mf}_TIME`] !=='undefined') { + return odho.timestamp >= Number(response_object.higestTimestampList[`${mf}_TIME`]-3600000); + } + }); + + latest_data[mf] = filtered_data; + } + + console.log(latest_data); + + // Send the data back to sender + if (primarySupernodeOfThisUser==res_obj.globalParams.senderFloId) { + latest_data.trader_flo_address = primarySupernodeOfThisUser; + latest_data.receiver_flo_address = res_obj.globalParams.senderFloId; + RM_RPC + .send_rpc + .call(this, "sync_primary_supernode_from_backup_supernode_response", latest_data) + .then(server_sync_response=>doSend(server_sync_response)); + } else { + latest_data.trader_flo_address = primarySupernodeOfThisUser; + latest_data.receiver_flo_address = res_obj.globalParams.senderFloId; + RM_RPC + .send_rpc + .call(this, "sync_backup_supernode_from_backup_supernode_response", latest_data) + .then(server_sync_response=>doSend(server_sync_response)); + } + + }); + + } + + break; + + case "validate_latest_db_hash": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const response_object = res_obj.params[0]; + if(typeof response_object.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; + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[primarySupernodeOfThisUser]; + + if(typeof backup_server_db_instance !== "object" + || backup_server_db_instance==localbitcoinplusplus.wallets.my_local_flo_address) { + 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('supernodesDbHash', response_object.id) + .then(my_db_hash_obj=>{ + if(typeof my_db_hash_obj=="object") { + if((my_db_hash_obj.vectorClock>=response_object.vectorClock) + || (my_db_hash_obj.timestamp>=response_object.timestamp)) { + + let diffs = []; // compare two objects and get the tables with difference in hash + + for (key in my_db_hash_obj) { + if (my_db_hash_obj.hasOwnProperty(key)) { + if (response_object[key] + && response_object[key] !== my_db_hash_obj[key] + && !['DBHash', 'data_of', 'id', 'timestamp', 'vectorClock'].includes(key)) { + diffs[key] = my_db_hash_obj[key]; + } + } + } + + localbitcoinplusplus.actions.get_sharable_db_data(diffs, primarySupernodeOfThisUser) + .then(function (su_db_data) { + if (typeof su_db_data == "object") { + su_db_data.trader_flo_address = primarySupernodeOfThisUser; + su_db_data.receiver_flo_address = res_obj.globalParams.senderFloId; + RM_RPC + .send_rpc + .call(this, "sync_backup_supernode_from_backup_supernode_response", su_db_data) + .then(server_sync_response=>doSend(server_sync_response)); + } + }); + + } + } + }); + + }); + + } + break; + + case "is_node_alive_request": + if(localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) { + reactor.dispatchEvent('nodeIsAlive', res_obj); + // Send your live status to Sender + const RM_RPC = new localbitcoinplusplus.rpc; + RM_RPC + .send_rpc + .call(this, "yup_i_am_awake", { + JOB: 'I_AM_ALIVE', + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address, + receiver_flo_address: res_obj.globalParams.senderFloId + }).then(req=>doSend(req)); + } + break; + + case "yup_i_am_awake": + if (res_obj.method=="yup_i_am_awake" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(localbitcoinplusplus.wallets.my_local_flo_public_key) + && (res_obj.params[0].receiver_flo_address == + localbitcoinplusplus.wallets.my_local_flo_address)) { + + reactor.dispatchEvent('nodeIsAlive', res_obj); + return; + } + break; + + case "store_backup_system_btc_reserves_private_keys": + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object" + && localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey) + && res_obj.globalParams.receiverFloId==localbitcoinplusplus.wallets.my_local_flo_address + ) { + const resp_data = res_obj.params[0]; + + try { + const btc_pk_str = localbitcoinplusplus.encrypt + .decryptMessage(resp_data.data.secret, resp_data.data.senderPublicKeyString); + + const btc_pk_obj = JSON.parse(btc_pk_str); + + const subject_user = btc_pk_obj.trader_flo_address || resp_data.trader_flo_address; + localbitcoinplusplus.kademlia.determineClosestSupernode(subject_user).then(get_su=>{ + const supernode_flo_id = get_su[0].data.id; + + if (typeof supernode_flo_id !=="string") throw new Error(`ERROR: Invalid FLO key.`); + + if (supernode_flo_id==localbitcoinplusplus.wallets.my_local_flo_address) { + updateinDB("system_btc_reserves_private_keys", + btc_pk_obj, btc_pk_obj.id, false, false); + } else if(typeof localbitcoinplusplus.newBackupDatabase.db[supernode_flo_id]=="object") { + localbitcoinplusplus.newBackupDatabase + .db[supernode_flo_id].backup_updateinDB("system_btc_reserves_private_keys", + btc_pk_obj, btc_pk_obj.id, false, false); + } else { + throw new Error(`ERROR: Failed to store backup system_btc_reserves_private_keys id ${btc_pk_obj.id}`); + } + }); + } catch(e) { + throw new Error(e); + } + + } + break; + + case "update_deposited_crypto_instance": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const res_data = res_obj.params[0]; + const backup_db = res_data.db_inst; + let _updateinDB = updateinDB; + try { + if (typeof backup_db=="string" && backup_db.length>0) { + if (localbitcoinplusplus.wallets.my_local_flo_address!==backup_db + && typeof localbitcoinplusplus.newBackupDatabase.db[backup_db] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[backup_db]; + _updateinDB = foreign_db.backup_updateinDB.bind(foreign_db); + } + + if (typeof res_data.deposit_data == "object") + _updateinDB('deposit', res_data.deposit_data, res_data.deposit_data.id, true, false); + if (typeof res_data.btc_reserve_data == "object") + _updateinDB('system_btc_reserves_private_keys', res_data.btc_reserve_data, + res_data.btc_reserve_data.id.id, true, false); + + } + } catch (e) { + console.error(e); + } + } + break; + + case "delete_deposited_crypto_instance": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const res_data = res_obj.params[0]; + const backup_db = res_data.db_inst; + let _removeinDB = removeinDB; + try { + if (typeof backup_db=="string" && backup_db.length>0) { + if (localbitcoinplusplus.wallets.my_local_flo_address!==backup_db + && typeof localbitcoinplusplus.newBackupDatabase.db[backup_db] == "object") { + const foreign_db = localbitcoinplusplus.newBackupDatabase.db[backup_db]; + _removeinDB = foreign_db.backup_removeinDB.bind(foreign_db); + } + if (typeof res_data.withdraw_btc_id == "string") _removeinDB('withdraw_btc', res_data.withdraw_btc_id); + if (typeof res_data.deposit_id == "string") _removeinDB('deposit', res_data.deposit_id); + if (typeof res_data.btc_reserve_id == "string") _removeinDB('system_btc_reserves_private_keys', res_data.btc_reserve_id); + } + } catch (e) { + console.error(e); + } + } + break; + + case "request_me_db_data": + if (localbitcoinplusplus.master_configurations.supernodesPubKeys + .includes(res_obj.nodePubKey)) { + const resp = res_obj.params[0]; + + if(localbitcoinplusplus.wallets.my_local_flo_address !== resp.receiver_flo_address) return; + + localbitcoinplusplus.actions + .sync_backup_supernode_from_backup_supernode( + localbitcoinplusplus.wallets.my_local_flo_address, + resp.trader_flo_address, + resp.db_inst + ); + } + break; + + default: + break; + } + + } + } catch(e) { + console.warn(e); + } + } } function onError(evt) { @@ -15245,12 +20528,8 @@ return msg; } - function doSend(message) { + function doSend(message, user_flo_id="") { - if(websocket.readyState!==1) { - console.warn("Websocket not ready to broadcast messages."); - //return; - } const request_array = ['send_back_shamirs_secret_supernode_pvtkey', 'retrieve_shamirs_secret_supernode_pvtkey', 'store_shamirs_secret_pvtkey_shares']; @@ -15262,13 +20541,11 @@ if (!request_array.includes(msgObj.method)) { const RM_WALLET = new localbitcoinplusplus.wallets; - if (typeof message !== "string") { - message = JSON.stringify(message); - } + message = JSON.stringify(msgObj); const message256hash = Crypto.SHA256(message); if(typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY !== "string") - throw new Error(`Private key could not be found.`); + throw new Error(`WARNING: Private key could not be found.`); const nodeSignedMessage = RM_WALLET.sign(message256hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY); @@ -15278,9 +20555,53 @@ finalMessage = JSON.stringify(msgObj); } + + // The message is for a specific supernode only + if (user_flo_id!=="" && user_flo_id.length>0) { + try { + if (typeof localbitcoinplusplus.backupWS[user_flo_id]=="object") { + wsConn = localbitcoinplusplus.backupWS[user_flo_id].ws_connection; + if(wsConn.readyState !== 1) { + let msg = "WARNING: Websocket not ready to broadcast messages."; + showMessage(msg); + console.warn(msg); + return; + } + wsConn.send(finalMessage); + } + } catch (error) { + showMessage(`ERROR: Failed to determine WS connection with ${user_flo_id}.`); + throw new Error(error); + } + + } else { + // The message is for usernodes and all backup supernodes + try { + websocket.send(finalMessage); - writeToScreen("SENT: " + finalMessage); - websocket.send(finalMessage); + for (const key in localbitcoinplusplus.backupWS) { + if (localbitcoinplusplus.backupWS.hasOwnProperty(key)) { + if (typeof localbitcoinplusplus.backupWS[key]=="object") { + const ws_conn = localbitcoinplusplus.backupWS[key].ws_connection; + if(ws_conn.readyState !== 1) { + let msg = "WARNING: Websocket not ready to broadcast messages."; + showMessage(msg); + console.warn(msg); + } else { + ws_conn.send(finalMessage); + } + } + } + } + + } catch(error) { + showMessage(`ERROR: Failed to determine WS connection with ${user_flo_id}.`); + throw new Error(error); + } + } + + console.log("SENT: " + finalMessage); + } function validateIncomingMessage(message) { @@ -15332,14 +20653,6 @@ }) } - function writeToScreen(message) { - // var pre = document.createElement("p"); - // pre.style.wordWrap = "break-word"; - // pre.innerHTML = message; - //output.appendChild(pre); - console.log(message); - } - /* Websocket Code Ends Here*/ @@ -15364,12 +20677,14 @@ myLocalFLOAddress: "", myLocalFLOPublicKey: "", myAddressTrustLevel: 1, + lastConnectedSupernode: "", + lastConnectedTime: "", }; const userPublicData = { trader_flo_address: null, trader_flo_pubKey: null, - trader_status: null, + trader_reputation: null, supernode_flo_public_key: null, timestamp: null }; @@ -15435,7 +20750,7 @@ trader_flo_address: null, utxo_addr: null, receiverBTCAddress: null, - receiverBTCEquivalentInCash: null, + receivingBTC: null, currency: null, product: null, change_adress: null, @@ -15452,25 +20767,28 @@ id: null, vectorClock: 0, data: null, + primary_supernode_flo_public_key: null, last_updated_on: null, } - const messages_table = { - id: null, - trader_flo_address: null, - supernode_flo_address: null, - timestamp: null, - message: null - } - const ipTable = { flo_public_key: null, temporary_ip: null } + const crypto_fiat_rates = { + id: null, + crypto_code: null, + currency_code: null, + rate: 0, + supernode_pub_key: null, + sign: null, + timestamp: +new Date() + } + var db; const DBName = "localbitcoinDB"; - const request = window.indexedDB.open(DBName, 3); + const request = window.indexedDB.open(DBName, 1); request.onerror = function (event) { //https://stackoverflow.com/questions/13972385/invalidstateerror-while-opening-indexeddb-in-firefox @@ -15481,6 +20799,7 @@ request.onsuccess = function (event) { db = request.result; + doShreeGanesh(); loadLocalDBData(); }; @@ -15602,7 +20921,7 @@ objectStore.createIndex('trader_flo_pubKey', 'trader_flo_pubKey', { unique: true }); - objectStore.createIndex('trader_status', 'trader_status', { + objectStore.createIndex('trader_reputation', 'trader_reputation', { unique: false }); } @@ -15617,16 +20936,9 @@ if (!db.objectStoreNames.contains('kBucketStore')) { var objectStore = db.createObjectStore('kBucketStore', { keyPath: "id" - }) - } - if (!db.objectStoreNames.contains('messages_table')) { - var objectStore = db.createObjectStore("messages_table", { - keyPath: 'id' - }); - objectStore.createIndex('trader_flo_address', 'trader_flo_address', { - unique: true - }); - objectStore.createIndex('supernode_flo_address', 'supernode_flo_address', { + }); + objectStore.createIndex('primary_supernode_flo_public_key' + , 'primary_supernode_flo_public_key', { unique: false }); } @@ -15649,9 +20961,20 @@ unique: false }); } + if (!db.objectStoreNames.contains('crypto_fiat_rates')) { + var objectStore = db.createObjectStore("crypto_fiat_rates", { + keyPath: 'id' + }); + objectStore.createIndex('currency_code', 'currency_code', { + unique: false + }); + objectStore.createIndex('crypto_code', 'crypto_code', { + unique: false + }); + } } - function readDB(tablename, id) { + function readDB(tablename, id, filter_deletables=true) { return new Promise((resolve, reject) => { var transaction = db.transaction([tablename]); var objectStore = transaction.objectStore(tablename); @@ -15662,8 +20985,17 @@ }; request.onsuccess = function (event) { - if (request.result) { - resolve(request.result); + if (request.result){ + if (filter_deletables==true) { + if(typeof request.result.is_deletable == "undefined") { + resolve(request.result); + } else { + resolve(); + } + } else { + resolve(request.result); + } + } else { resolve(); } @@ -15671,7 +21003,7 @@ }); } - function readDBbyIndex(tablename, index, indexValue) { + function readDBbyIndex(tablename, index, indexValue, filter_deletables=true) { return new Promise((resolve, reject) => { var transaction = db.transaction([tablename]); var objectStore = transaction.objectStore(tablename); @@ -15684,7 +21016,13 @@ let cursor = event.target.result; if (cursor) { if (cursor.value[index] == indexValue) { - response.push(cursor.value); + if (filter_deletables==true) { + if (typeof cursor.value.is_deletable == "undefined") { + response.push(cursor.value); + } + } else { + response.push(cursor.value); + } } cursor.continue(); } else { @@ -15694,14 +21032,25 @@ }); } - function readAllDB(tablename) { + function readAllDB(tablename, filter_deletables=true, limit=0) { return new Promise((resolve, reject) => { let response = []; + // let keyRangeValue; + // if (limit>0) { + // keyRangeValue = IDBKeyRange.lowerBound(limit); + // } var objectStore = db.transaction(tablename).objectStore(tablename); objectStore.openCursor().onsuccess = function (event) { let cursor = event.target.result; if (cursor) { - response.push(cursor.value); + if (filter_deletables==true) { + if (typeof cursor.value.is_deletable == "undefined") { + response.push(cursor.value); + } + } else { + response.push(cursor.value); + } + cursor.continue(); } else { resolve(response); @@ -15713,6 +21062,7 @@ async function addDB(tablename, dbObject) { try { if(typeof dbObject.vectorClock == "undefined") dbObject.vectorClock = 0; + if(typeof dbObject.timestamp == "undefined") dbObject.timestamp = + new Date(); let request = db.transaction([tablename], "readwrite") let store = request.objectStore(tablename) await store.add(dbObject); @@ -15724,18 +21074,46 @@ } } - async function updateinDB(tablename, Obj, key) { + async function updateinDB(tablename, Obj, key, updateByVectorClock=false, increaseVectorClock=true) { + // updateByVectorClock==true will not return Obj back. + // Return value will be undefined try { if(typeof Obj.vectorClock == "undefined") { Obj.vectorClock = 0; + } else if(increaseVectorClock === false) { + // leave the vector clock field unchanged } else { Obj.vectorClock += 1; } + if (typeof Obj.timestamp !== "number") { + Obj.timestamp = + new Date(); + } var request = db.transaction([tablename], "readwrite") let store = request.objectStore(tablename) - await store.put(Obj); - await request.complete; + if (updateByVectorClock===true) { + if (typeof key=="undefined") { + key = Obj[store.keyPath]; + } + let objectStoreRequest = store.get(key); + objectStoreRequest.onsuccess = + async function(event) { + var myRecord = objectStoreRequest.result; + if(typeof myRecord !=="object") { + Obj.vectorClock = (typeof Obj.vectorClock=="number" ? Obj.vectorClock:0); + await store.put(Obj); + await request.complete; + } else if (myRecord.vectorClock < Obj.vectorClock) { + await store.put(Obj); + await request.complete; + } + } + + } else { + await store.put(Obj); + await request.complete; + } return Obj; + } catch (error) { return new Error(error); } @@ -15935,7 +21313,7 @@ objectStore.createIndex('trader_flo_pubKey', 'trader_flo_pubKey', { unique: true }); - objectStore.createIndex('trader_status', 'trader_status', { + objectStore.createIndex('trader_reputation', 'trader_reputation', { unique: false }); } @@ -15952,20 +21330,31 @@ keyPath: "id" }) } - if (!this.db.objectStoreNames.contains('messages_table')) { - var objectStore = this.db.createObjectStore("messages_table", { + if (!this.db.objectStoreNames.contains('ipTable')) { + var objectStore = this.db.createObjectStore("ipTable", { + keyPath: 'flo_public_key' + }); + } + if (!this.db.objectStoreNames.contains('myClosestSupernodes')) { + var objectStore = this.db.createObjectStore("myClosestSupernodes", { keyPath: 'id' }); objectStore.createIndex('trader_flo_address', 'trader_flo_address', { unique: true }); - objectStore.createIndex('supernode_flo_address', 'supernode_flo_address', { + objectStore.createIndex('ip', 'ip', { unique: false }); } - if (!db.objectStoreNames.contains('ipTable')) { - var objectStore = db.createObjectStore("ipTable", { - keyPath: 'flo_public_key' + if (!db.objectStoreNames.contains('crypto_fiat_rates')) { + var objectStore = db.createObjectStore("crypto_fiat_rates", { + keyPath: 'id' + }); + objectStore.createIndex('currency_code', 'currency_code', { + unique: false + }); + objectStore.createIndex('crypto_code', 'crypto_code', { + unique: false }); } @@ -15973,19 +21362,28 @@ }, - backup_readDB(tablename, id) { + backup_readDB(tablename, id, filter_deletables=true) { return new Promise((resolve, reject) => { this.transaction = this.db.transaction([tablename]); - var objectStore = transaction.objectStore(tablename); + var objectStore = this.transaction.objectStore(tablename); this.request = objectStore.get(id); + let parent_request = this.request; this.request.onerror = function (event) { reject("Unable to retrieve data from database!"); }; this.request.onsuccess = function (event) { - if (this.request.result) { - resolve(this.request.result); + if (parent_request.result) { + if (filter_deletables==true) { + if(typeof parent_request.result.is_deletable == "undefined") { + resolve(parent_request.result); + } else { + resolve(); + } + } else { + resolve(parent_request.result); + } } else { resolve(); } @@ -15993,7 +21391,7 @@ }); }, - backup_readDBbyIndex(tablename, index, indexValue) { + backup_readDBbyIndex(tablename, index, indexValue, filter_deletables=true) { return new Promise((resolve, reject) => { this.transaction = this.db.transaction([tablename]); var objectStore = this.transaction.objectStore(tablename); @@ -16006,7 +21404,13 @@ let cursor = event.target.result; if (cursor) { if (cursor.value[index] == indexValue) { - response.push(cursor.value); + if (filter_deletables==true) { + if(typeof cursor.value.is_deletable == "undefined") { + response.push(cursor.value); + } + } else { + response.push(cursor.value); + } } cursor.continue(); } else { @@ -16016,14 +21420,21 @@ }); }, - backup_readAllDB(tablename) { + backup_readAllDB(tablename, filter_deletables=true) { return new Promise((resolve, reject) => { let response = []; var objectStore = this.db.transaction(tablename).objectStore(tablename); objectStore.openCursor().onsuccess = function (event) { let cursor = event.target.result; if (cursor) { - response.push(cursor.value); + if (filter_deletables==true) { + if (typeof cursor.value.is_deletable == "undefined") { + response.push(cursor.value); + } + } else { + response.push(cursor.value); + } + cursor.continue(); } else { resolve(response); @@ -16034,6 +21445,8 @@ async backup_addDB(tablename, dbObject) { try { + if(typeof dbObject.vectorClock == "undefined") dbObject.vectorClock = 0; + if(typeof dbObject.timestamp == "undefined") dbObject.timestamp = + new Date(); this.request = this.db.transaction([tablename], "readwrite") let store = this.request.objectStore(tablename) await store.add(dbObject); @@ -16045,13 +21458,52 @@ } }, - async backup_updateinDB(tablename, Obj, key) { + async backup_updateinDB(tablename, Obj, key, updateByVectorClock=false, increaseVectorClock=true) { try { + if(typeof Obj.vectorClock == "undefined") { + Obj.vectorClock = 0; + } else if(increaseVectorClock === false) { + // leave the vector clock field unchanged + } else { + Obj.vectorClock += 1; + } + if (typeof Obj.timestamp !== "number") { + Obj.timestamp = + new Date(); + } + let that = this; this.request = this.db.transaction([tablename], "readwrite") let store = this.request.objectStore(tablename) - await store.put(Obj); - await this.request.complete; - return Obj; + + if (updateByVectorClock===true) { + if (typeof key=="undefined") { + key = Obj[store.keyPath]; + } + let objectStoreRequest = store.get(key); + objectStoreRequest.onsuccess = + function(event) { + return new Promise(async (resolve, reject)=>{ + var myRecord = objectStoreRequest.result; + var myRecord = objectStoreRequest.result; + if(typeof myRecord !=="object") { + Obj.vectorClock = (typeof Obj.vectorClock=="number" ? Obj.vectorClock:0); + await store.put(Obj); + await that.request.complete; + } + else if (myRecord.vectorClock < Obj.vectorClock) { + await store.put(Obj); + await that.request.complete; + } + }); + resolve(Obj); + return; + } + + } else { + await store.put(Obj); + await that.request.complete; + return Obj; + } + } catch (error) { return new Error(error); } @@ -16060,10 +21512,31 @@ async backup_removeinDB(tablename, id) { try { this.request = this.db.transaction([tablename], "readwrite") + let store = this.request.objectStore(tablename) - await store.delete(id); - await this.request.complete; - return id; + let objectStoreRequest = store.get(id); + let that = this; + objectStoreRequest.onsuccess = + function(event) { + return new Promise(async (resolve, reject)=>{ + var myRecord = objectStoreRequest.result; + + if(typeof myRecord =="object") { + myRecord.vectorClock += 1; + + // https://stackoverflow.com/a/39333479/5348972 + const modifiedRecord = (({ id, timestamp, vectorClock }) => + ({ id, timestamp, vectorClock }))(myRecord); + + modifiedRecord.is_deletable = true; + + await store.put(modifiedRecord); + await that.request.complete; + return id; + } + }); + reject(false); + } } catch (error) { return new Error(error); } @@ -16074,11 +21547,22 @@ this.request = this.db.transaction([tablename], "readwrite") .objectStore(tablename); var index = this.request.index(indexName); + let parent_request = this.request; this.request = index.openCursor(IDBKeyRange.only(indexValue)); - this.request.onsuccess = function () { - var cursor = this.request.result; + this.request.onsuccess = async function (event) { + var cursor = event.target.result; if (cursor) { - cursor.delete(); + let myRecord = cursor.value; + myRecord.vectorClock += 1; + + // https://stackoverflow.com/a/39333479/5348972 + const modifiedRecord = (({ id, timestamp, vectorClock }) => + ({ id, timestamp, vectorClock }))(myRecord); + + modifiedRecord.is_deletable = true; + + await parent_request.put(modifiedRecord); + await parent_request.complete; cursor.continue(); } else { resolve(true); @@ -16087,21 +21571,8 @@ request.onerror = function (e) { reject(e); } - }) + }); }, - - async backup_removeAllinDB(tablename) { - try { - this.request = this.db.transaction([tablename], "readwrite") - var objectStore = this.request.objectStore(tablename); - var objectStoreRequest = await objectStore.clear(); - await this.request.complete; - console.info("All the data entry has been removed from your database " + tablename); - return tablename; - } catch (error) { - return new Error(error); - } - } } @@ -16112,6 +21583,7 @@ const doShreeGanesh = () => { try { var rm_configs = localbitcoinplusplus.actions.fetch_configs(async function (...fetch_configs_res) { + localbitcoinplusplus.is_ui_loaded = false; showMessage(`Connecting to Supernode server. Please wait...`); window.bitjs = []; // Launch bitjs localbitcoinplusplus.master_configurations.tradableAsset1.map(asset => bitjslib(asset)); @@ -16123,7 +21595,6 @@ throw new Error(`Failed to fetch configurations: ${error}`); } } - doShreeGanesh(); @@ -16491,9 +22048,9 @@ showMessage(err_msg); throw new Error(err_msg); } - let buytrade = RM_TRADE.place_order("buy", idbData.myLocalFLOAddress, - selectListCrypto.value, selectListFiat.value, parseFloat(selectListAmount.value)); - doSend(buytrade); + RM_TRADE.place_order("buy", idbData.myLocalFLOAddress, + selectListCrypto.value, selectListFiat.value, parseFloat(selectListAmount.value)) + .then(buytrade=>doSend(buytrade)); } trade_sell_button.onclick = function (event) { @@ -16503,9 +22060,9 @@ showMessage(err_msg); throw new Error(err_msg); } - let selltrade = RM_TRADE.place_order("sell", idbData.myLocalFLOAddress, - selectListCrypto.value, selectListFiat.value, parseFloat(selectListAmount.value)); - doSend(selltrade); + RM_TRADE.place_order("sell", idbData.myLocalFLOAddress, + selectListCrypto.value, selectListFiat.value, parseFloat(selectListAmount.value)) + .then(selltrade=>doSend(selltrade)); } } @@ -16542,21 +22099,28 @@ } // Create a select input for trade amount - let tradeAmountSelect = document.createElement('select'); + // let tradeAmountSelect = document.createElement('select'); + // tradeAmountSelect.id = "trade_amount_select"; + // asset_box.appendChild(tradeAmountSelect); + // if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' && + // localbitcoinplusplus.master_configurations.validTradingAmount.length > 0) { + // let tradeAmountSelectArray = JSON.parse(JSON.stringify(localbitcoinplusplus.master_configurations.validTradingAmount)); + // tradeAmountSelectArray.unshift("Select Asset Amount"); + // for (var i = 0; i < tradeAmountSelectArray.length; i++) { + // var option = document.createElement("option"); + // option.value = tradeAmountSelectArray[i]; + // option.text = tradeAmountSelectArray[i]; + // tradeAmountSelect.appendChild(option); + // } + // } + + + let tradeAmountSelect = document.createElement('input'); + tradeAmountSelect.setAttribute("type", "text"); + tradeAmountSelect.setAttribute("placeholder", "Specify Asset Amount"); tradeAmountSelect.id = "trade_amount_select"; asset_box.appendChild(tradeAmountSelect); - if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' && - localbitcoinplusplus.master_configurations.validTradingAmount.length > 0) { - let tradeAmountSelectArray = JSON.parse(JSON.stringify(localbitcoinplusplus.master_configurations.validTradingAmount)); - tradeAmountSelectArray.unshift("Select Asset Amount"); - for (var i = 0; i < tradeAmountSelectArray.length; i++) { - var option = document.createElement("option"); - option.value = tradeAmountSelectArray[i]; - option.text = tradeAmountSelectArray[i]; - tradeAmountSelect.appendChild(option); - } - } - + let currencySelect = document.createElement('select'); currencySelect.id = `withdraw_fiat_currency`; asset_box.appendChild(currencySelect); @@ -16595,12 +22159,10 @@ showMessage(err_msg); throw new Error(err_msg); } - if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' && - localbitcoinplusplus.master_configurations.validTradingAmount.includes(tradeAmount) && - (typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' && - localbitcoinplusplus.master_configurations.tradableAsset1.includes(asset_type) || - typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined' && - localbitcoinplusplus.master_configurations.tradableAsset2.includes(asset_type)) + if (typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' && + localbitcoinplusplus.master_configurations.tradableAsset1.includes(asset_type) || + typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined' && + localbitcoinplusplus.master_configurations.tradableAsset2.includes(asset_type) ) { RM_TRADE.depositAsset(asset_type, tradeAmount, fiatCurrency, userFLOaddress); } else { @@ -16627,9 +22189,15 @@ showMessage(err_msg); throw new Error(err_msg); } - if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' && - localbitcoinplusplus.master_configurations.validTradingAmount.includes(tradeAmount) && - typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' && + if (localbitcoinplusplus.master_configurations.tradableAsset2.includes(asset_type)) { + if (typeof localbitcoinplusplus.master_configurations.validTradingAmount !== 'undefined' + && !localbitcoinplusplus.master_configurations.validTradingAmount.includes(tradeAmount)) { + err_msg = "Invalid Fiat Value."; + showMessage(err_msg); + throw new Error(err_msg); + } + } + if (typeof localbitcoinplusplus.master_configurations.tradableAsset1 !== 'undefined' && typeof localbitcoinplusplus.master_configurations.tradableAsset2 !== 'undefined' && localbitcoinplusplus.master_configurations.tradableAsset1 .concat(localbitcoinplusplus.master_configurations.tradableAsset2).includes(asset_type) @@ -16650,9 +22218,12 @@ refresh_crypto_status_btn.appendChild(refresh_crypto_status_btn_text); asset_button_box.appendChild(refresh_crypto_status_btn); refresh_crypto_status_btn.addEventListener('click', function () { - let refresh_deposit_status = RM_RPC.send_rpc.call(this, - "refresh_deposit_status_request", {receiver_flo_address: localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS}); - doSend(refresh_deposit_status); + RM_RPC.send_rpc.call(this, + "refresh_deposit_status_request", + { + receiver_flo_address: localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, + trader_flo_address: localbitcoinplusplus.wallets.my_local_flo_address + }).then(refresh_deposit_status=>doSend(refresh_deposit_status)); }); } @@ -16763,19 +22334,24 @@ const RM_TRADE = new localbitcoinplusplus.trade; RM_TRADE.sendTransaction(send_crypto_type.value, utxo_addr_input.value, utxo_addr_wif_input.value, receiver_address_input.value, - receiving_crypto_amount_input.value, null, change_adress_input.value, + receiving_crypto_amount_input.value, change_adress_input.value, async function (res) { console.log(res); - if (typeof res == "string" && res.length > 0) { + if (typeof res == "object") { try { - let resp_obj = JSON.parse(res); + let resp_obj = JSON.parse(res.txid); let resp_txid = resp_obj.txid.result || resp_obj.txid; let msg = `Transaction Id for your deposited crypto asset: ${resp_txid}`; showMessage(msg); + + msg = `Signed Raw Tx for your deposited crypto asset: ${res.signedTxHash}`; + showMessage(msg); return true; } catch (error) { console.warn(error); showMessage(error); + showMessage(`INFO: We could not broadcast your transaction. Please broadcast + this Signed Raw Tx manually yourself: ${res.signedTxHash}`); } } }); @@ -16806,14 +22382,31 @@ - +