diff --git a/.gitignore b/.gitignore index 1a39658..de930dc 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,13 @@ examples.mk json-rpc/ supernode/playground/ playground -supernode/index1.html +supernode/test/in +supernode/test/in/index1.html +supernode/test/in/index2.html +supernode/test/in/index3.html +supernode/test/in/index4.html supernode/flosend.html -supernode/index1 (copy).html \ No newline at end of file +supernode/index1 (copy).html +supernode/websocket_chat +.vscode/ +supernode/test/ diff --git a/supernode/index.html b/supernode/index.html index 88adc94..183b165 100644 --- a/supernode/index.html +++ b/supernode/index.html @@ -530,11 +530,11 @@ @@ -9163,8 +9163,6 @@ })(); } - - reactor.registerEvent('added'); reactor.addEventListener('added', function (someObject) { console.log('Added fired with data ' + someObject); @@ -9180,6 +9178,10 @@ console.log('Updated fired with data ' + someObject); }); + reactor.registerEvent('bucket_full'); + reactor.addEventListener('bucket_full', function (someObject) { + console.log('Bucket full ' + someObject); + }); /* //Sample Usage @@ -9200,26702 +9202,422 @@ //Checking if existing NodeID can be used //This first block of if will initialize the configuration of KBucket //Add Events, Messaging between different K-Buckets, and attach relevant distributed data - if (typeof KBucketOptions == "undefined" || !KBucketOptions) { - var KBucketOptions = {}; - var sanitizedFLOAddress = new Uint8Array(20); - KBucketOptions.localNodeId = window.crypto.getRandomValues(sanitizedFLOAddress); - - } - - if (typeof KBucket == "undefined" || !KBucket) { - (function (options) { - - - - var buf = new Uint8Array(20); - var randomBytes = window.crypto.getRandomValues(buf); - - - /** - * @param {Uint8Array} array1 - * @param {Uint8Array} array2 - * @return {Boolean} - */ - function arrayEquals(array1, array2) { - if (array1 === array2) { - return true - } - if (array1.length !== array2.length) { - return false - } - for (let i = 0, length = array1.length; i < length; ++i) { - if (array1[i] !== array2[i]) { - return false - } - } + /** + * @param {Uint8Array} array1 + * @param {Uint8Array} array2 + * @return {Boolean} + */ + function arrayEquals(array1, array2) { + if (array1 === array2) { return true } - - function createNode() { - return { - contacts: [], - dontSplit: false, - left: null, - right: null + if (array1.length !== array2.length) { + return false + } + for (let i = 0, length = array1.length; i < length; ++i) { + if (array1[i] !== array2[i]) { + return false } } - - function ensureInt8(name, val) { - if (!(val instanceof Uint8Array)) { - throw new TypeError(name + ' is not a Uint8Array') - } - } - - /** - * Implementation of a Kademlia DHT k-bucket used for storing - * contact (peer node) information. - * - * @extends EventEmitter - */ - function KBucket(options = {}) { - /** - * `options`: - * `distance`: Function - * `function (firstId, secondId) { return distance }` An optional - * `distance` function that gets two `id` Uint8Arrays - * and return distance (as number) between them. - * `arbiter`: Function (Default: vectorClock arbiter) - * `function (incumbent, candidate) { return contact; }` An optional - * `arbiter` function that givent two `contact` objects with the same `id` - * returns the desired object to be used for updating the k-bucket. For - * more details, see [arbiter function](#arbiter-function). - * `localNodeId`: Uint8Array An optional Uint8Array representing the local node id. - * If not provided, a local node id will be created via `randomBytes(20)`. - * `metadata`: Object (Default: {}) Optional satellite data to include - * with the k-bucket. `metadata` property is guaranteed not be altered by, - * it is provided as an explicit container for users of k-bucket to store - * implementation-specific data. - * `numberOfNodesPerKBucket`: Integer (Default: 20) The number of nodes - * that a k-bucket can contain before being full or split. - * `numberOfNodesToPing`: Integer (Default: 3) The number of nodes to - * ping when a bucket that should not be split becomes full. KBucket will - * emit a `ping` event that contains `numberOfNodesToPing` nodes that have - * not been contacted the longest. - * - * @param {Object=} options optional - */ - - this.localNodeId = options.localNodeId || randomBytes - this.numberOfNodesPerKBucket = options.numberOfNodesPerKBucket || 20 - this.numberOfNodesToPing = options.numberOfNodesToPing || 3 - this.distance = options.distance || KBucket.distance - // use an arbiter from options or vectorClock arbiter by default - this.arbiter = options.arbiter || KBucket.arbiter - this.metadata = Object.assign({}, options.metadata) - - ensureInt8('option.localNodeId as parameter 1', this.localNodeId) - - this.root = createNode() - - - /** - * Default arbiter function for contacts with the same id. Uses - * contact.vectorClock to select which contact to update the k-bucket with. - * Contact with larger vectorClock field will be selected. If vectorClock is - * the same, candidat will be selected. - * - * @param {Object} incumbent Contact currently stored in the k-bucket. - * @param {Object} candidate Contact being added to the k-bucket. - * @return {Object} Contact to updated the k-bucket with. - */ - this.arbiter = function (incumbent, candidate) { - return incumbent.vectorClock > candidate.vectorClock ? incumbent : candidate - } - - /** - * Default distance function. Finds the XOR - * distance between firstId and secondId. - * - * @param {Uint8Array} firstId Uint8Array containing first id. - * @param {Uint8Array} secondId Uint8Array containing second id. - * @return {Number} Integer The XOR distance between firstId - * and secondId. - */ - this.distance = function (firstId, secondId) { - let distance = 0 - let i = 0 - const min = Math.min(firstId.length, secondId.length) - const max = Math.max(firstId.length, secondId.length) - for (; i < min; ++i) { - distance = distance * 256 + (firstId[i] ^ secondId[i]) - } - for (; i < max; ++i) distance = distance * 256 + 255 - return distance - } - - /** - * Adds a contact to the k-bucket. - * - * @param {Object} contact the contact object to add - */ - this.add = function (contact) { - ensureInt8('contact.id', (contact || {}).id) - - let bitIndex = 0 - let node = this.root - - while (node.contacts === null) { - // this is not a leaf node but an inner node with 'low' and 'high' - // branches; we will check the appropriate bit of the identifier and - // delegate to the appropriate node for further processing - node = this._determineNode(node, contact.id, bitIndex++) - } - - // check if the contact already exists - const index = this._indexOf(node, contact.id) - if (index >= 0) { - this._update(node, index, contact) - return this - } - - if (node.contacts.length < this.numberOfNodesPerKBucket) { - node.contacts.push(contact) - reactor.dispatchEvent('added', contact) - return this - } - - // the bucket is full - if (node.dontSplit) { - // we are not allowed to split the bucket - // we need to ping the first this.numberOfNodesToPing - // in order to determine if they are alive - // only if one of the pinged nodes does not respond, can the new contact - // be added (this prevents DoS flodding with new invalid contacts) - this.emit('ping', node.contacts.slice(0, this.numberOfNodesToPing), contact) - return this - } - - this._split(node, bitIndex) - return this.add(contact) - } - - /** - * Get the n closest contacts to the provided node id. "Closest" here means: - * closest according to the XOR metric of the contact node id. - * - * @param {Uint8Array} id Contact node id - * @param {Number=} n Integer (Default: Infinity) The maximum number of - * closest contacts to return - * @return {Array} Array Maximum of n closest contacts to the node id - */ - this.closest = function (id, n = Infinity) { - ensureInt8('id', id) - - if ((!Number.isInteger(n) && n !== Infinity) || n <= 0) { - throw new TypeError('n is not positive number') - } - - let contacts = [] - - for (let nodes = [this.root], bitIndex = 0; nodes.length > 0 && contacts.length < n;) { - const node = nodes.pop() - if (node.contacts === null) { - const detNode = this._determineNode(node, id, bitIndex++) - nodes.push(node.left === detNode ? node.right : node.left) - nodes.push(detNode) - } else { - contacts = contacts.concat(node.contacts) - } - } - - return contacts - .map(a => [this.distance(a.id, id), a]) - .sort((a, b) => a[0] - b[0]) - .slice(0, n) - .map(a => a[1]) - } - - /** - * Counts the total number of contacts in the tree. - * - * @return {Number} The number of contacts held in the tree - */ - this.count = function () { - // return this.toArray().length - let count = 0 - for (const nodes = [this.root]; nodes.length > 0;) { - const node = nodes.pop() - if (node.contacts === null) nodes.push(node.right, node.left) - else count += node.contacts.length - } - return count - } - - /** - * Determines whether the id at the bitIndex is 0 or 1. - * Return left leaf if `id` at `bitIndex` is 0, right leaf otherwise - * - * @param {Object} node internal object that has 2 leafs: left and right - * @param {Uint8Array} id Id to compare localNodeId with. - * @param {Number} bitIndex Integer (Default: 0) The bit index to which bit - * to check in the id Uint8Array. - * @return {Object} left leaf if id at bitIndex is 0, right leaf otherwise. - */ - this._determineNode = function (node, id, bitIndex) { - // *NOTE* remember that id is a Uint8Array and has granularity of - // bytes (8 bits), whereas the bitIndex is the bit index (not byte) - - // id's that are too short are put in low bucket (1 byte = 8 bits) - // (bitIndex >> 3) finds how many bytes the bitIndex describes - // bitIndex % 8 checks if we have extra bits beyond byte multiples - // if number of bytes is <= no. of bytes described by bitIndex and there - // are extra bits to consider, this means id has less bits than what - // bitIndex describes, id therefore is too short, and will be put in low - // bucket - const bytesDescribedByBitIndex = bitIndex >> 3 - const bitIndexWithinByte = bitIndex % 8 - if ((id.length <= bytesDescribedByBitIndex) && (bitIndexWithinByte !== 0)) { - return node.left - } - - const byteUnderConsideration = id[bytesDescribedByBitIndex] - - // byteUnderConsideration is an integer from 0 to 255 represented by 8 bits - // where 255 is 11111111 and 0 is 00000000 - // in order to find out whether the bit at bitIndexWithinByte is set - // we construct (1 << (7 - bitIndexWithinByte)) which will consist - // of all bits being 0, with only one bit set to 1 - // for example, if bitIndexWithinByte is 3, we will construct 00010000 by - // (1 << (7 - 3)) -> (1 << 4) -> 16 - if (byteUnderConsideration & (1 << (7 - bitIndexWithinByte))) { - return node.right - } - - return node.left - } - - /** - * Get a contact by its exact ID. - * If this is a leaf, loop through the bucket contents and return the correct - * contact if we have it or null if not. If this is an inner node, determine - * which branch of the tree to traverse and repeat. - * - * @param {Uint8Array} id The ID of the contact to fetch. - * @return {Object|Null} The contact if available, otherwise null - */ - this.get = function (id) { - ensureInt8('id', id) - - let bitIndex = 0 - - let node = this.root - while (node.contacts === null) { - node = this._determineNode(node, id, bitIndex++) - } - - // index of uses contact id for matching - const index = this._indexOf(node, id) - return index >= 0 ? node.contacts[index] : null - } - - /** - * Returns the index of the contact with provided - * id if it exists, returns -1 otherwise. - * - * @param {Object} node internal object that has 2 leafs: left and right - * @param {Uint8Array} id Contact node id. - * @return {Number} Integer Index of contact with provided id if it - * exists, -1 otherwise. - */ - this._indexOf = function (node, id) { - for (let i = 0; i < node.contacts.length; ++i) { - if (arrayEquals(node.contacts[i].id, id)) return i - } - - return -1 - } - - /** - * Removes contact with the provided id. - * - * @param {Uint8Array} id The ID of the contact to remove. - * @return {Object} The k-bucket itself. - */ - this.remove = function (id) { - ensureInt8('the id as parameter 1', id) - - let bitIndex = 0 - let node = this.root - - while (node.contacts === null) { - node = this._determineNode(node, id, bitIndex++) - } - - const index = this._indexOf(node, id) - if (index >= 0) { - const contact = node.contacts.splice(index, 1)[0] - reactor.dispatchEvent('removed', contact) - } - - return this - } - - /** - * Splits the node, redistributes contacts to the new nodes, and marks the - * node that was split as an inner node of the binary tree of nodes by - * setting this.root.contacts = null - * - * @param {Object} node node for splitting - * @param {Number} bitIndex the bitIndex to which byte to check in the - * Uint8Array for navigating the binary tree - */ - this._split = function (node, bitIndex) { - node.left = createNode() - node.right = createNode() - - // redistribute existing contacts amongst the two newly created nodes - for (const contact of node.contacts) { - this._determineNode(node, contact.id, bitIndex).contacts.push(contact) - } - - node.contacts = null // mark as inner tree node - - // don't split the "far away" node - // we check where the local node would end up and mark the other one as - // "dontSplit" (i.e. "far away") - const detNode = this._determineNode(node, this.localNodeId, bitIndex) - const otherNode = node.left === detNode ? node.right : node.left - otherNode.dontSplit = true - } - - /** - * Returns all the contacts contained in the tree as an array. - * If this is a leaf, return a copy of the bucket. `slice` is used so that we - * don't accidentally leak an internal reference out that might be - * accidentally misused. If this is not a leaf, return the union of the low - * and high branches (themselves also as arrays). - * - * @return {Array} All of the contacts in the tree, as an array - */ - this.toArray = function () { - let result = [] - for (const nodes = [this.root]; nodes.length > 0;) { - const node = nodes.pop() - if (node.contacts === null) nodes.push(node.right, node.left) - else result = result.concat(node.contacts) - } - return result - } - - /** - * Updates the contact selected by the arbiter. - * If the selection is our old contact and the candidate is some new contact - * then the new contact is abandoned (not added). - * If the selection is our old contact and the candidate is our old contact - * then we are refreshing the contact and it is marked as most recently - * contacted (by being moved to the right/end of the bucket array). - * If the selection is our new contact, the old contact is removed and the new - * contact is marked as most recently contacted. - * - * @param {Object} node internal object that has 2 leafs: left and right - * @param {Number} index the index in the bucket where contact exists - * (index has already been computed in a previous - * calculation) - * @param {Object} contact The contact object to update. - */ - this._update = function (node, index, contact) { - // sanity check - if (!arrayEquals(node.contacts[index].id, contact.id)) { - throw new Error('wrong index for _update') - } - - const incumbent = node.contacts[index] - - /***************Change made by Abhishek*************/ - const selection = this.arbiter(incumbent, contact) - //const selection = localbitcoinplusplus.kademlia.arbiter(incumbent, contact); - // if the selection is our old contact and the candidate is some new - // contact, then there is nothing to do - if (selection === incumbent && incumbent !== contact) return - - node.contacts.splice(index, 1) // remove old contact - node.contacts.push(selection) // add more recent contact version - /***************Change made by Abhishek*************/ - reactor.dispatchEvent('updated', { - ...incumbent, - ...selection - }) - //reactor.dispatchEvent('updated', incumbent.concat(selection)) - } - } - - window.KBucket = new KBucket(options) - - })(KBucketOptions); - - } - - - + @@ -36347,12 +10069,21 @@ // 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, - #!#supernodes=127.0.0.1,212.88.88.2#!#MASTER_NODE=023B9F60692A17FAC805D012C5C8ADA3DD19A980A3C5F0D8A5B3500CC54D6E8B75 - #!#MASTER_RECEIVING_ADDRESS=oVRq2nka1GtALQT8pbuLHAGjqAQ7PAo6uy#!#validTradingAmount=10000,50000,100000,#!#btcTradeMargin=5000 - #!#supernodesPubKeys=02B2D2F40EC5BC9D336BAF14017D629B3A96892D5E86095923A50A734E2364FB34, + #!#validTradingAmount=10000,50000,100000,#!#btcTradeMargin=5000 + #!#supernodesPubKeys=0315C3A20FE7096CC2E0F81A80D5F1A687B8F9EFA65242A0B0881E1BA3EE7D7D53, + 03F7493F11B8E44B9798CD434D20FBE7FA34B9779D144984889D11A17C56A18742,039B4AA00DBFC0A6631DE6DA83526611A0E6B857D3579DF840BBDEAE8B6898E3B6, #!#externalFiles={"d3js":"58f54395efa8346e8e94d12609770f66b916897e7f4e05f6c98780cffa5c70a3"}, - #!#ShamirsMaxShares=8#!#supernodeSeeds={"ranchimall1":{"ip":"ranchimall1.duckdns.org","port":"9002","kbucketId":"oJeYebjgWV4Hszj5oP7sfXcj1U98P5M6q4"}, - "ranchimall2":{"ip":"ranchimall1.duckdns.org","port":"9003","kbucketId":"oH47fmrVEbSDcoXJG28BQZ4kzUpD9VTM6U"}}`; + #!#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"}}`; let floAssetsArray = RMAssets.split('#!#'); @@ -36364,7 +10095,7 @@ let k = assets_string.split('='); if (k[1].indexOf(',') > 0) { k[1] = k[1].split(',') - .map(val => !isNaN(val) ? parseFloat(val) : val) + .map(val => !isNaN(val) ? parseFloat(val) : val.trim()) .filter(v => ![null, "", undefined, NaN].includes(v)); } else if (!isNaN(k[1])) { k[1] = parseFloat(k[1]); @@ -36392,7 +10123,8 @@ let sync_request = RM_RPC.send_rpc.call(this, "sync_with_supernode", { "trader_flo_address": trader_flo_address, - "job": "SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB" + "job": "SYNC_MY_LOCAL_DB_WITH_SUPERNODE_DB", + "receiver_flo_address": localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, }); doSend(sync_request); }, @@ -36424,6 +10156,7 @@ 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 @@ -36472,7 +10205,7 @@ t += ``; }); - t += ` `; + t += ``; modalWindow(t); } @@ -36502,29 +10235,62 @@ k.splice(-4, 4) return Crypto.util.bytesToHex(k) }, - addContact: function (id, data) { - const nodeId = new Uint8Array(id.length) - for (let i = 0, len = nodeId.length; i < len; ++i) { - nodeId[i] = id.charCodeAt(i) - } + launchKBucket: function(flo_id) { + return new Promise((resolve, reject)=>{ + try { + const KBucketId = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', flo_id) + const kbOptions = { localNodeId: KBucketId } + window.KBucket = new BuildKBucket(kbOptions); + window.supernodeKBucket = new BuildKBucket(kbOptions); + resolve(true); + } catch (error) { + reject(error); + } + }) + }, + addContact: function (id, data, KB=KBucket) { const contact = { - id: nodeId, + id: id, data: data }; - KBucket.add(contact) + KB.add(contact) }, - addNewUserNodeInKbucket: function (blockchain, address, data) { - const decodedId = this.decodeBase58Address(blockchain, address); - const addNewUserNode = this.addContact(decodedId, data); - return addNewUserNode; + addNewUserNodeInKbucket: function(blockchain, address, data, KB=KBucket) { + let decodedId = address; + try { + decodedId = this.floIdToKbucketId(blockchain, address); + } catch(e) { + decodedId = address; + } + const addNewUserNode = this.addContact(decodedId, data, KB); + return {decodedId:decodedId, data:data}; + }, + addNewUserNodeInKbucketAndDB: function (blockchain, address, data, KB=KBucket) { + let kbuck = this.addNewUserNodeInKbucket(blockchain, address, data, KB); + readDB('kBucketStore', kbuck.decodedId).then(kres=>{ + if (typeof kres=="object") { + kres.data = data; + } else { + kbuckObj={ + id: kbuck.decodedId, + vectorClock: 0, + data: kbuck.data, + last_updated_on: + new Date(), + } + kres = kbuckObj; + } + updateinDB('kBucketStore', kres) + .then(ms=>showMessage(`INFO: Added/Updated a node in DB.`)) + .catch(e=>{showMessage(`ERROR: Failed to add a node in DB.`)}); + return Promise.resolve(kbuck); + }); }, floIdToKbucketId: function (blockchain, address) { const decodedId = this.decodeBase58Address(blockchain, address); - const nodeId = new Uint8Array(decodedId.length) - for (let i = 0, len = nodeId.length; i < len; ++i) { - nodeId[i] = decodedId.charCodeAt(i) - } - return nodeId; + const nodeIdBigInt = new BigInteger(decodedId, 16); + const nodeIdBytes = nodeIdBigInt.toByteArrayUnsigned(); + const nodeIdNewInt8Array = new Uint8Array(nodeIdBytes); + return nodeIdNewInt8Array; }, arbiter: function (incumbent, candidate) { // we create a new object so that our selection is guaranteed to replace @@ -36544,58 +10310,128 @@ let pubKeyBytes = Crypto.util.hexToBytes(pubKey); return Crypto.util.bytesToBase64(pubKeyBytes); }, - getSupernodeSeed: function (flo_addr, flo_pub_key) { - return new Promise((resolve, reject) => { + restoreKbucket: function(flo_addr, blockchain="FLO_TEST", KB=KBucket) { + return new Promise((resolve, reject)=>{ + readAllDB('kBucketStore') + .then(dbObject => { + if (typeof dbObject=="object") { + let su_flo_addr_array = localbitcoinplusplus.master_configurations.supernodesPubKeys + .map(pubk=>bitjs.FLO_TEST.pubkey2address(pubk)); + // Prevent supernode to re-added in kbucket + dbObject + .filter(f=>!su_flo_addr_array.includes(f.data.id)) + .map(dbObj=>{ + this.addNewUserNodeInKbucket(blockchain, flo_addr, dbObj.data, KB); + }); + } else { + reject(`Failed to restore kBucket.`); + } + resolve(dbObject); + }); + }) + }, + restoreSupernodeKBucket: function() { + return new Promise((resolve, reject)=>{ const supernodeSeeds = localbitcoinplusplus.master_configurations.supernodeSeeds; - if (typeof supernodeSeeds !== "object") throw new Error( - "Failed to get supernode seeds."); + if (typeof supernodeSeeds !== "object") reject("Failed to get supernode seeds."); let supernodeSeedsObj = JSON.parse(supernodeSeeds); - const contactId = localbitcoinplusplus.kademlia.newBase64DiscoverId(flo_pub_key); - - const seedContactArray = { - id: contactId, // Base64 encoding - data: { - publicKey: flo_pub_key, - floId: flo_addr, - msg: `Hello from ${flo_addr}!` - }, - }; - Object.entries(supernodeSeedsObj).map(seedObj=>{ - localbitcoinplusplus.kademlia.addNewUserNodeInKbucket( + let kbuck = this.addNewUserNodeInKbucket("FLO_TEST", seedObj[1].kbucketId, + { id: seedObj[1].kbucketId }, supernodeKBucket); + }); + }) + }, + updateClosestSupernodeSeeds: function(flo_addr) { + return new Promise(async (resolve, reject) => { + let nearestSupernodeAddresslist = await localbitcoinplusplus.kademlia.addClosestSupernodeInDB(flo_addr); + await removeAllinDB('myClosestSupernodes'); + nearestSupernodeAddresslist.map((nearestSupernodeAddress, index)=>{ + updateinDB('myClosestSupernodes', { + id: index+1, + ip: nearestSupernodeAddress.ip, + port: nearestSupernodeAddress.port, + trader_flo_address: nearestSupernodeAddress.kbucketId + }).then(res=>showMessage(`INFO: Updated closest supernodes list successfully.`)); + }); + }); + }, + getSupernodeSeed: function (flo_addr) { + return new Promise(async (resolve, reject) => { + let nearestSupernodeAddresslist = await readAllDB('myClosestSupernodes'); + if (nearestSupernodeAddresslist.length<1) { + nearestSupernodeAddresslist = await this.updateClosestSupernodeSeeds(flo_addr); + } + //this.restoreSupernodeKBucket(flo_addr, "FLO_TEST", supernodeKBucket); + resolve(nearestSupernodeAddresslist); + }); + }, + isNodePresentInMyKbucket: function(flo_id, KB=KBucket) { + return new Promise((resolve, reject)=>{ + let kArray = KB.toArray(); + let kArrayFloIds = kArray.map(k=>k.data.id); + if (kArrayFloIds.includes(flo_id)) { + resolve(true); + } else { + reject(false); + } + }); + }, + determineClosestSupernode: function(flo_addr, KB=supernodeKBucket, n=1) { + return new Promise((resolve, reject)=>{ + let msg = ``; + if (typeof KB !== "object") { + msg = `ERROR: Supernode KBucket not found.`; + showMessage(msg); + reject(msg); + return false; + } + + try { + let isFloIdUint8 = flo_addr instanceof Uint8Array; + if (!isFloIdUint8) { + flo_addr = localbitcoinplusplus.kademlia.floIdToKbucketId('FLO_TEST', flo_addr); + } + const closestSupernode = KB.closest(flo_addr, n); + resolve(closestSupernode); + return true; + } catch (error) { + showMessage(error); + reject(error); + return false; + } + }) + }, + addClosestSupernodeInDB: function(flo_addr, KB=KBucket) { + return new Promise((resolve, reject)=>{ + const supernodeSeeds = localbitcoinplusplus.master_configurations.supernodeSeeds; + if (typeof supernodeSeeds !== "object") reject("Failed to get supernode seeds."); + let supernodeSeedsObj = JSON.parse(supernodeSeeds); + + Object.entries(supernodeSeedsObj).map(seedObj=>{ + console.log(seedObj); + localbitcoinplusplus.kademlia.addNewUserNodeInKbucketAndDB( "FLO_TEST", seedObj[1].kbucketId, - { id: seedObj[1].kbucketId, vectorClock: 0 }); + { id: seedObj[1].kbucketId }); }); let currentNodeBucketId = localbitcoinplusplus.kademlia .floIdToKbucketId("FLO_TEST", flo_addr); - let nearestSupernode = KBucket.closest(currentNodeBucketId, 1); - - nearestSupernodeAddress = Object.values(supernodeSeedsObj).filter(seed=> - nearestSupernode[0].data.id == seed.kbucketId); + let nearestSupernode = KB.closest(currentNodeBucketId, 4); + let nearestSupernodeIds = nearestSupernode.map(f=>f.data.id); + let supernodeSeedsArray = Object.values(supernodeSeedsObj) + .filter(seed=>nearestSupernodeIds.includes(seed.kbucketId)) + .sort(function(a, b){ + return nearestSupernodeIds.indexOf(a.kbucketId) - nearestSupernodeIds.indexOf(b.kbucketId); + }); - seedContactArray.transport = { - host: nearestSupernodeAddress[0].ip, - port: nearestSupernodeAddress[0].port, - id: nearestSupernodeAddress[0].kbucketId + if (supernodeSeedsArray.length>0) { + resolve(supernodeSeedsArray); + } else { + reject(false); } - - const nodeDiscoveryOptions = { - seeds: seedContactArray - } - - const kdiscover = new tristanDiscover(nodeDiscoveryOptions); - - try { - let supernodeAddressUrl = `ws://${kdiscover.seeds.transport.host}:${kdiscover.seeds.transport.port}`; - console.log(supernodeAddressUrl); - resolve(supernodeAddressUrl); - } catch (error) { - reject(error); - } - }); - }, + }) + } } @@ -36739,20 +10575,21 @@ // This function is only useful when sender and receiver are both online. // If receiver is not online he might never get the message - messageBroadcasting: function (message, flo_id) { + messageBroadcasting: function (message, flo_id, rpc_subject="messageBroadcasting") { readDB('userPublicData', flo_id).then((res) => { pubKey = res.trader_flo_pubKey; - let foo = localbitcoinplusplus.encrypt.encryptMessage(message, pubKey); + let data = localbitcoinplusplus.encrypt.encryptMessage(message, pubKey); const RM_RPC = new localbitcoinplusplus.rpc; let bar = RM_RPC .send_rpc - .call(this, "messageBroadcasting", foo); + .call(this, rpc_subject, {data: data, receiver_flo_address: flo_id}); 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 .send_rpc .call(this, "MessageForMiddleman", dataToBeSentToReceiver); @@ -36762,6 +10599,166 @@ } + + + @@ -37008,10 +11031,19 @@ 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 request.toString(); }, - filter_legit_requests: function (callback) { + filter_legit_requests: function (flo_id=null, callback) { if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY === "string" && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length > 0 ) { @@ -37020,11 +11052,27 @@ if (typeof user_keys == "object" && typeof user_keys.pubKeyHex == "string") { if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( user_keys.pubKeyHex)) { - return callback(true); + 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) + .then(my_closest_su=>{ + if (user_keys.address === my_closest_su[0].data.id) { + return callback(true); + } + }); + } else { + return callback(false); + } } } + } else { + return callback(false); } - return callback(false); }, async receive_rpc_response(request) { @@ -37043,8 +11091,8 @@ request.response = {}; let err_msg; - if (method=="sync_with_supernode") { - RM_RPC.filter_legit_requests(function (is_valid_request) { + 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) { @@ -37055,6 +11103,7 @@ 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 .send_rpc .call(this, "server_sync_response", @@ -37064,10 +11113,11 @@ }); } }); - } + } + + RM_RPC.filter_legit_requests(params.trader_flo_address, async function (is_valid_request) { + if (is_valid_request !== true) return false; - if (localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( - localbitcoinplusplus.wallets.my_local_flo_public_key)) { try { // CHECK HERE IF USER IS INDULGED IN ANY MORE TRADE. IF TRUE RETURN ERROR await readAllDB("deposit").then(function (res) { @@ -37085,6 +11135,7 @@ .send_rpc .call(this, "supernode_message", { "trader_flo_id": respective_trader_id, + "receiver_flo_address": respective_trader_id, "server_msg": request.response }); doSend(server_response); @@ -37098,8 +11149,7 @@ // Check if user id is in deposit or withdraw. If true prevent him from trading await readAllDB('withdraw_cash').then(function (res) { if (typeof res == "object") { - let check_deposit_withdraw_id_array = res.filter(f => f.status === - 2) + 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) { @@ -37112,6 +11162,7 @@ .send_rpc .call(this, "supernode_message", { "trader_flo_id": respective_trader_id, + "receiver_flo_address": respective_trader_id, "server_msg": server_msg }); doSend(server_response); @@ -37126,12 +11177,12 @@ } catch (error) { throw new Error(error); } - } + }); switch (method) { case "trade_buy": - RM_RPC.filter_legit_requests(async function (is_valid_request) { + RM_RPC.filter_legit_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); @@ -37144,6 +11195,7 @@ 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") { let buy_request_response = RM_RPC .send_rpc @@ -37170,7 +11222,7 @@ }); break; case "trade_sell": - RM_RPC.filter_legit_requests(async function (is_valid_request) { + RM_RPC.filter_legit_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); @@ -37185,6 +11237,7 @@ this, ...request.params, 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 .send_rpc .call(this, @@ -37215,7 +11268,7 @@ // already processed above break; case "deposit_asset_request": - RM_RPC.filter_legit_requests(function (is_valid_request) { + RM_RPC.filter_legit_requests(params.trader_flo_address, function (is_valid_request) { if (is_valid_request !== true) return false; @@ -37263,9 +11316,7 @@ params.depositing_amount, params.currency, params.product); - let receivedTradeInfo = { - ...params - }; + let receivedTradeInfo = { ...params }; if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == "undefined") { @@ -37287,43 +11338,28 @@ ) { let receivedTradeInfoHash = - Crypto.SHA256(JSON.stringify( - receivedTradeInfo)); + Crypto.SHA256(JSON.stringify(receivedTradeInfo)); - receivedTradeInfo[ - "depositDataHash"] = - receivedTradeInfoHash; - receivedTradeInfo[ - "order_validator_sign"] = - RM_WALLET.sign( - receivedTradeInfoHash, - localbitcoinplusplus.wallets - .MY_SUPERNODE_PRIVATE_KEY + 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; + receivedTradeInfo["order_validator_public_key"] = su_data.myLocalFLOPublicKey; try { const this_btc_pvt_key = - generate_btc_keys_for_requester - .privateKeyWIF; + generate_btc_keys_for_requester.privateKeyWIF; const this_btc_tx_key = - Crypto.util.randomBytes( - 64); + Crypto.util.randomBytes(64); const this_btc_pvt_key_shamirs_secret = - RM_WALLET.createShamirsSecretShares( - this_btc_pvt_key, - 10, 5); + 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 - ); + addDB("deposit", receivedTradeInfo); // Send the address to the requester let @@ -37333,6 +11369,7 @@ msg: `Please send ${params.product} ${params.bitcoinToBePaid} to the following addres: ${generate_btc_keys_for_requester.address}.`, data: receivedTradeInfo }; + deposit_response_object.receiver_flo_address = params.trader_flo_address; let deposit_request_response = RM_RPC @@ -37341,9 +11378,7 @@ "deposit_asset_request_response", deposit_response_object ); - doSend( - deposit_request_response - ); + doSend(deposit_request_response); let this_btc_pvt_key_shamirs_secret_array = @@ -37449,7 +11484,8 @@ error: false, method: "deposit_asset_request_response", msg: `Please send the ${params.product} to ${generate_btc_keys_for_requester.address}.`, - data: receivedTradeInfo + data: receivedTradeInfo, + receiver_flo_address: params.trader_flo_address, }; let deposit_request_response = @@ -37471,9 +11507,7 @@ params.status = 1; // IMPORTANT - If deposit is a fiat make sure product and currency are both same params.currency = params.product; - let receivedTradeInfo = { - ...params - }; + let receivedTradeInfo = { ...params }; readDB("localbitcoinUser", "00-01").then( function (su_data) { @@ -37517,86 +11551,50 @@ if (typeof withdrawers_list == "object") { if ( - withdrawers_list - .length > - 0) { - withdrawers_list - .filter( - wd => - wd - .currency == - params - .currency - ).map( - function ( - withdrawer - ) { + withdrawers_list.length > 0) { + withdrawers_list.filter( + wd => wd.currency == + params.currency).map( + function (withdrawer) { if ( - withdrawer - .withdraw_amount == - params - .depositing_amount && - withdrawer - .currency == - params - .currency + 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 = + 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); + + receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account + updateinDB( + "deposit", + receivedTradeInfo, receivedTradeInfo - .trader_flo_address; - updateinDB - ( - "withdraw_cash", - withdrawer, - withdrawer - .trader_flo_address - ); + .trader_flo_address + ); - receivedTradeInfo - .status = - 2; // withdrawer found. Now deposit money to his account - updateinDB - ( - "deposit", - receivedTradeInfo, - receivedTradeInfo - .trader_flo_address - ); + let withdrawer_bank_account = withdrawer.receivinAddress; - let - withdrawer_bank_account = - withdrawer - .receivinAddress; + 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, + receiver_flo_address: receivedTradeInfo.trader_flo_address, + }; - 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 - }; 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 - ); + doSend(deposit_request_response); return true; } else { err_msg = "Deposit request failed: We could not find a withdrawer."; @@ -37612,7 +11610,8 @@ error: false, method: "deposit_asset_request_response", msg: `Plese send the money to following bank address: "System determined bank".`, - data: receivedTradeInfo + data: receivedTradeInfo, + receiver_flo_address: receivedTradeInfo.trader_flo_address, }; let deposit_request_response = @@ -37624,9 +11623,7 @@ deposit_response_object ); - receivedTradeInfo - .status = - 2; // withdrawer found. Now deposit money to his account + receivedTradeInfo.status = 2; // withdrawer found. Now deposit money to his account updateinDB ( "deposit", @@ -37635,9 +11632,7 @@ .trader_flo_address ); - doSend( - deposit_request_response - ); + doSend(deposit_request_response); return true; } } @@ -37661,7 +11656,7 @@ }); break; case "withdraw_request_method": - RM_RPC.filter_legit_requests(async function (is_valid_request) { + RM_RPC.filter_legit_requests(params.trader_flo_address, async function (is_valid_request) { if (is_valid_request !== true) return false; @@ -37883,8 +11878,7 @@ "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 } ); doSend @@ -37960,7 +11954,7 @@ withdraw_amount: withdrawing_cash_amount, currency: params.currency, receivinAddress: bank_details, - status: 1 // withdraw request called + status: 1, // withdraw request called } readDB("localbitcoinUser", "00-01").then( @@ -38008,6 +12002,8 @@ 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 = RM_RPC.send_rpc @@ -38043,7 +12039,7 @@ break; case "retrieve_shamirs_secret_btc_pvtkey": - RM_RPC.filter_legit_requests(function (is_valid_request) { + RM_RPC.filter_legit_requests(null, 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 !== @@ -38081,8 +12077,7 @@ withdraw_res.receiverBTCEquivalentInCash, withdraw_res.currency, withdraw_res.product); - EqCryptoWd = parseFloat( - EqCryptoWd); + EqCryptoWd = parseFloat(EqCryptoWd); let transaction_key = btc_reserves.supernode_transaction_key; @@ -38181,8 +12176,8 @@ updateUserCryptoBalanceObject = { updatedBTCBalanceObject: res_bal, updatedBTCBalanceObjectSign: res_obj_sign, - trader_flo_address: withdraw_res - .trader_flo_address + trader_flo_address: withdraw_res.trader_flo_address, + receiver_flo_address: withdraw_res.trader_flo_address, } const @@ -38285,22 +12280,27 @@ case "superNodeSignedAddUserPublicData": if (typeof params == "object" && typeof params.data == "object") { - if (typeof params.su_pubKey == "string" && localbitcoinplusplus + RM_RPC.filter_legit_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_status: params.data.trader_status, - 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) { - addDB('userPublicData', params.data); - return true; + 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, + 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) { + addDB('userPublicData', params.data); + return true; + } } - } + }); } break; @@ -38429,7 +12429,8 @@ "trader_flo_address": this.user_flo_address, "product": this.product, "currency": this.currency, - "buy_price": this.buy_price + "buy_price": this.buy_price, + "receiver_flo_address":localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } const RM_RPC = new localbitcoinplusplus.rpc; let placeNewOrder = RM_RPC.send_rpc.call(this, this @@ -38636,7 +12637,8 @@ depositor_public_key: null, operation_type: "deposit", order_validator_public_key: null, - product: assetType + product: assetType, + receiver_flo_address:localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } const RM_RPC = new localbitcoinplusplus.rpc; let deposit_request = RM_RPC.send_rpc.call(this, @@ -38676,7 +12678,8 @@ operation_type: "withdraw", order_validator_public_key: null, product: assetType, - currency: currency + currency: currency, + receiver_flo_address:localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } const RM_RPC = new localbitcoinplusplus.rpc; let withdraw_request = RM_RPC.send_rpc.call(this, @@ -39147,8 +13150,8 @@ "seller_btc_data": sellerBTCResponseObject, "data_hash": hashed_data, "supernode_sign": signed_data, - "supernodePubKey": user_data - .myLocalFLOPublicKey + "supernodePubKey": user_data.myLocalFLOPublicKey, + "receiver_flo_address": "BROADCAST_TO_NODES", } callback(response_for_client); return true; @@ -39178,7 +13181,8 @@ trade_id: trade_id, signed_trade_id: signed_trade_id, trader_flo_address: trader_flo_address, - trade_type: trade_type + trade_type: trade_type, + receiver_flo_address:localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS, } let cancel_trade_request = RM_RPC.send_rpc.call(this, @@ -39316,13 +13320,13 @@ * @param method A String containing the name of the method to be invoked. * @param params (optional) A Structured value that holds the parameter values to be used during the invocation of the method. */ - JSON_RPC.Request = function (method, params) { + JSON_RPC.Request = function (method, params, globalParams={}) { this.jsonrpc = "2.0"; this.method = method; if (typeof params !== "undefined") { this.params = params; } - + this.globalParams = globalParams; this.id = id++; }; @@ -39336,6 +13340,7 @@ delete this.method; // remove the method name delete this.params; // remove the params delete this.error; // remove error state if it exists + delete this.globalParams; // remove globalParams this._result = result; } }); @@ -39350,6 +13355,7 @@ delete this.method; // remove the method name delete this.params; // remove the params delete this.result; // remove result state if it exists + delete this.globalParams; // remove globalParams if it exists this._error = error; } }); @@ -39368,6 +13374,7 @@ if (this.params !== undefined) rpc.params = this.params; if (this.result !== undefined) rpc.result = this.result; if (this.error !== undefined) rpc.error = this.error; + if (this.globalParams !== undefined) rpc.globalParams = this.globalParams; return JSON.stringify(rpc); }; @@ -39377,12 +13384,15 @@ * @param method A String containing the name of the method to be invoked. * @param params (optional) A Structured value that holds the parameter values to be used during the invocation of the method. */ - JSON_RPC.Notification = function (method, params) { + JSON_RPC.Notification = function (method, params, globalParams) { this.jsonrpc = "2.0"; this.method = method; if (typeof params !== "undefined") { this.params = params; } + if (typeof globalParams !== "undefined") { + this.globalParams = globalParams; + } }; /** @@ -39393,7 +13403,8 @@ var rpc = { jsonrpc: this.jsonrpc, method: this.method, - params: this.params + params: this.params, + globalParams: this.globalParams, }; return JSON.stringify(rpc); @@ -39486,17 +13497,19 @@ function kickInit() { output = document.getElementById("output_div"); const RM_WALLET = new localbitcoinplusplus.wallets; + const BACKUP_DB = localbitcoinplusplus.newBackupDatabase.db = []; + const backUpSupernodesWS = localbitcoinplusplus.backupWS = []; return new Promise(resolve => { readDB("localbitcoinUser", "00-01").then(async function (idbData) { + if (typeof idbData.myLocalFLOPublicKey == "undefined" || idbData.myLocalFLOPublicKey .trim() == '') { let user_pvt_key = prompt( "Please Enter a valid FLO private key if you have any. Else leave blank." ); - if (user_pvt_key.trim() == "" || user_pvt_key.length < 1) user_pvt_key = - null; + if (user_pvt_key.trim() == "" || user_pvt_key.length < 1) user_pvt_key = null; let newKeys = RM_WALLET.generateFloKeys(user_pvt_key); if (typeof newKeys == 'object' && newKeys.privateKeyWIF.length > 0 && @@ -39504,35 +13517,56 @@ localbitcoinplusplusObj.myLocalFLOAddress = newKeys.address; localbitcoinplusplusObj.myLocalFLOPublicKey = newKeys.pubKeyHex; + // launch KBuckets + launchKBuckects = await localbitcoinplusplus.kademlia.launchKBucket(newKeys.address); + + if (!launchKBuckects) { + const kmsg = `ERROR: Failed to build KBuckets. System cannot proceed further.`; + showMessage(kmsg); + throw new Error(kmsg); + } + updateinDB("localbitcoinUser", localbitcoinplusplusObj, "00-01"); wsUri = await localbitcoinplusplus.kademlia.getSupernodeSeed( - localbitcoinplusplusObj.myLocalFLOAddress, - localbitcoinplusplusObj.myLocalFLOPublicKey); + localbitcoinplusplusObj.myLocalFLOAddress); - await startWebSocket(wsUri); - - // Add new user node in Kademlia - addDB('kBucketStore', { - id: localbitcoinplusplusObj.myLocalFLOAddress, - data: { - id: localbitcoinplusplusObj.myLocalFLOAddress, - vectorClock: 0 - }, - }).then(dbObj => { + // Add close supernodes to KBucket + wsUri.map(d=>{ localbitcoinplusplus.kademlia.addNewUserNodeInKbucket( - "FLO_TEST", - dbObj.id, dbObj.data); - let addNewKNode = localbitcoinplusplus.rpc.prototype - .send_rpc - .call(this, "addNewKbucketNode", { - newKbucketNode: dbObj - }); - doSend(addNewKNode); - }).catch(e=>console.warn(e)); + "FLO_TEST", d.trader_flo_address, { id: d.trader_flo_address }) + 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()); + .then(() => privateKeyBuilder()) + // .finally(()=>{ + // if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY=='string' + // && localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY.length>0) { + // loadExternalFiles(); + // dataBaseUIOperations(); + // } + // }); + + // 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(); + + let dbname = `su_backup_${uri.trader_flo_address}`; + BACKUP_DB[uri.trader_flo_address] = new newBackupDB(dbname); + BACKUP_DB[uri.trader_flo_address].createNewDB(); + }); resolve(true); return; @@ -39544,15 +13578,414 @@ } } - wsUri = await localbitcoinplusplus.kademlia.getSupernodeSeed(idbData.myLocalFLOAddress, - idbData.myLocalFLOPublicKey); + // launch KBucekts + launchKBuckects = await localbitcoinplusplus.kademlia.launchKBucket(idbData.myLocalFLOAddress); - resolve(startWebSocket(wsUri)); + if (!launchKBuckects) { + const kmsg = `ERROR: Failed to build KBuckets. System cannot proceed further.`; + showMessage(kmsg); + throw new Error(kmsg); + } + + wsUri = await localbitcoinplusplus.kademlia.getSupernodeSeed(idbData.myLocalFLOAddress); + + // Add close supernodes to KBucket + wsUri.map(d=>{ + localbitcoinplusplus.kademlia.addNewUserNodeInKbucket( + "FLO_TEST", d.trader_flo_address, { id: d.trader_flo_address }) + showMessage(`INFO: Added Supernode Id ${d.trader_flo_address} to KBucket.`); + }); + + localbitcoinplusplus.kademlia.restoreSupernodeKBucket(); + + // Connect with primary supernodes + startWebSocket(`ws://${wsUri[0].ip}:${wsUri[0].port}`); + + // rebuild private key + await privateKeyBuilder(); + + //loadExternalFiles(); + //dataBaseUIOperations(); + + localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS = wsUri[0].trader_flo_address; + + // Connect with backup supernodes + wsUri.filter((uri, index)=>{ + if(index>0 && 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(); + }); + + resolve(true); }); }); } + const backupSupernodesWebSocketObject = localbitcoinplusplus.backupWS = function(ws_url) { + this.ws_url = ws_url; + this.ws_connection = null; + } + backupSupernodesWebSocketObject.prototype = { + + async handle_backup_server_messages(evt) { + var response = evt.data; + console.log('backup response: '+response); + + var res_pos = response.indexOf('{'); + if (res_pos >= 0) { + 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_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 "backup_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_requests(su_backup_db_data.trader_flo_address, + function (is_valid_request) { + if(!is_valid_request) return false; + + if (typeof localbitcoinplusplus.wallets.my_local_flo_address !== "string" || + su_backup_db_data.receiver_flo_address !== localbitcoinplusplus.wallets.my_local_flo_address + ) return false; + + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[su_backup_db_data.trader_flo_address]; + + 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": + 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[su_backup_db_data.trader_flo_address]; + + 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) { + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[su_backup_db_data.trader_flo_address]; + + 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" && 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[su_backup_db_data.trader_flo_address]; + + 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") { + 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[su_backup_db_data.trader_flo_address]; + + 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") { + let cancel_request = res_obj.params[0]; + if (cancel_request.job == "cancel_trade_request") { + const backup_server_db_instance = localbitcoinplusplus.newBackupDatabase.db[su_backup_db_data.trader_flo_address]; + + 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": + 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[su_backup_db_data.trader_flo_address]; + + 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") { + 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[su_backup_db_data.trader_flo_address]; + + 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; + + } + } + } catch(e) { + console.warn(e); + } + } + }, + + connectWS() { + this.ws_connection = new WebSocket(this.ws_url); + this.ws_connection.onopen = function (evt) { + showMessage(`Connected to backup Supernode sever: ${this.ws_url}.`); + }.bind(this); + this.ws_connection.onclose = function (evt) { + showMessage(`Disconnected to backup Supernode sever: ${this.ws_url}.`); + }.bind(this); + this.ws_connection.onmessage = function (evt) { + this.handle_backup_server_messages(evt); + }.bind(this); + this.ws_connection.onerror = function (evt) { + console.error(evt); + }; + }, + } + function startWebSocket(wsUri) { return new Promise((resolve, reject) => { websocket = new WebSocket(wsUri); @@ -39572,16 +14005,16 @@ } function onOpen(evt) { - loadExternalFiles(); - dataBaseUIOperations(); + showMessage(`Connected successfully to Supernode: ws://${wsUri[0].ip}.${wsUri[0].port}`); writeToScreen("CONNECTED"); } function onClose(evt) { + showMessage(`Disconnected to Supernode sever: ws://${wsUri[0].ip}.${wsUri[0].port}`); writeToScreen("DISCONNECTED"); } - function onMessage(evt) { + async function onMessage(evt) { var response = evt.data; var res_pos = response.indexOf('{'); if (res_pos >= 0) { @@ -39589,7 +14022,12 @@ try { var res_obj = JSON.parse(res); - if (typeof res_obj.method !== undefined) { + 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; @@ -39625,19 +14063,24 @@ 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) { - // Add buy order - addDB("buyOrders", buyOrders_data).then(() => { - showMessage(`Your buy order is placed successfully.`); - }); + 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.`); + }); + } } - } + }); } break; case "trade_sell": @@ -39647,20 +14090,24 @@ 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) { - // Add buy order - addDB("sellOrders", sellOrders_data).then(() => { - showMessage(`Your sell order is placed successfully.`); - });; + 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.`); + });; + } } - } + }); } break; case "sync_with_supernode": @@ -39739,6 +14186,7 @@ let counterTraderAccountAddress = `

Please pay the amount to following address:

${resp.msg}

`; + showMessage(counterTraderAccountAddress); modalWindow(counterTraderAccountAddress); } }); @@ -39804,35 +14252,40 @@ `${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); - } + RM_RPC.filter_legit_requests(trade_balance_res.trade_infos.buyer_flo_id, + function (is_valid_request) { + if (is_valid_request !== true) return 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); + 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); + 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); + } } } - } + }); } break; case "store_shamirs_secret_pvtkey_shares": @@ -39842,31 +14295,58 @@ break; case "send_back_shamirs_secret_supernode_pvtkey": 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 + readDB("supernode_private_key_chunks", res_obj.params[0].chunk_val) + .then(function (res) { + if (typeof res=="object") { + let send_pvtkey_req = RM_RPC .send_rpc .call(this, "retrieve_shamirs_secret_supernode_pvtkey", { private_key_chunk: res }); - doSend(send_pvtkey_req); + doSend(send_pvtkey_req); + return; + } else { + let send_pvtkey_req = RM_RPC + .send_rpc + .call(this, "retrieve_shamirs_secret_supernode_pvtkey", ""); + doSend(send_pvtkey_req); + return; + } }); } 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); - return; + 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.wallets.MY_SUPERNODE_PRIVATE_KEY.length>0 && runUIFunc==true) { + 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") { @@ -39906,9 +14386,8 @@ } break; case "deposit_withdraw_user_claim": - RM_RPC.filter_legit_requests(function (is_valid_request) { - if (is_valid_request !== true) return false; - if (typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY == + + 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") { @@ -39917,7 +14396,10 @@ let withdraw_order_id = user_claim_id[0]; let user_id = user_claim_id[1]; - let deposit_withdraw_user_claim_obj = { + 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 } @@ -39934,8 +14416,7 @@ readDB("withdraw_cash", withdraw_order_id).then(async function ( withdraw_data) { if (typeof withdraw_data == "object") { - if (withdraw_data.trader_flo_address == - user_id) { + if (withdraw_data.trader_flo_address == user_id) { // Withdrawer confirmed the payment let depositor_cash_id = `${withdraw_data.depositor_flo_id}_${withdraw_data.currency}`; @@ -40003,6 +14484,8 @@ update_cash_balance_obj.withdraw_id = withdraw_data.id; + update_cash_balance_obj.receiver_flo_address = user_id; + let update_cash_balance_req = RM_RPC .send_rpc .call(this, @@ -40037,6 +14520,9 @@ 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; + let update_withdraw_cash_obj = RM_RPC .send_rpc .call(this, @@ -40046,10 +14532,11 @@ } return true; } - }); - } + }); + } + }); + } - }); break; case "update_all_withdraw_cash_depositor_claim": if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { @@ -40101,61 +14588,35 @@ break; case "add_user_public_data": - RM_RPC.filter_legit_requests(function (is_valid_request) { - if (is_valid_request !== true) return false; - let supernode_flo_public_key = localbitcoinplusplus.wallets.my_local_flo_public_key; - if (!localbitcoinplusplus.master_configurations.supernodesPubKeys.includes( - supernode_flo_public_key)) { - throw new Error("Failed to identify as supernode."); - } + RM_RPC.filter_legit_requests(res_obj.params[0].public_data.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; - try { - let flo_address = bitjs.FLO_TEST.pubkey2address(req_data.trader_flo_pubKey); + if (typeof res_obj.params == "object" && typeof res_obj.params[0] == "object") { + let req_data = res_obj.params[0].public_data; + 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) { + 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_status: 0, + timestamp: +new Date() + } + + addDB('userPublicData', public_req_object); - 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_status: 0, - timestamp: +new Date() } - - addDB('userPublicData', public_req_object); - - let public_req_object_str = JSON.stringify(public_req_object); - let public_req_object_hash = Crypto.SHA256( - public_req_object_str); - let public_req_object_sign = RM_WALLET.sign( - public_req_object_hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY - ); - - let userPublicDataResponseObject = { - data: public_req_object, - data_hash: public_req_object_hash, - sign: public_req_object_sign, - su_pubKey: localbitcoinplusplus.wallets.my_local_flo_public_key - } - - let send_pvtkey_req = RM_RPC - .send_rpc - .call(this, "superNodeSignedAddUserPublicData", - userPublicDataResponseObject); - - doSend(send_pvtkey_req); - + } catch (error) { + throw new Error('Invalid public key and flo address combination.'); } - } catch (error) { - throw new Error('Invalid public key and flo address combination.'); } - } - }); + }); break; case "superNodeSignedAddUserPublicData": @@ -40179,23 +14640,20 @@ break; case "update_external_file_request": - RM_RPC.filter_legit_requests(is_valid_request => { + RM_RPC.filter_legit_requests(res_obj.params[0].user_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( "Unknown user"); - if (!localbitcoinplusplus.master_configurations.supernodesPubKeys - .includes(localbitcoinplusplus.wallets.my_local_flo_public_key)) throw new Error( - "Unauthorized server."); - let server_pubkey = localbitcoinplusplus.wallets.my_local_flo_public_key; if (typeof update_script_request.file_to_update == "string") { readDB("external_files", update_script_request.file_to_update).then( file_details => { - if (typeof file_details.content == "string" && 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 @@ -40203,11 +14661,12 @@ .MY_SUPERNODE_PRIVATE_KEY); response_from_sever = RM_RPC.send_rpc .call(this, "update_external_file_server_response", { - user_flo_address: update_script_request.user_flo_address, + trader_flo_address: update_script_request.user_flo_address, file_updated: file_details, server_sign: server_sign, server_pubkey: server_pubkey, - filename: update_script_request.file_to_update + filename: update_script_request.file_to_update, + receiver_flo_address: update_script_request.user_flo_address }); doSend(response_from_sever); } @@ -40221,11 +14680,12 @@ .MY_SUPERNODE_PRIVATE_KEY); response_from_sever = RM_RPC.send_rpc .call(this, "update_external_file_server_response", { - user_flo_address: update_script_request.user_flo_address, + trader_flo_address: update_script_request.user_flo_address, file_updated: file_details, server_sign: server_sign, server_pubkey: server_pubkey, - filename: "UPDATE_ALL_FILES" + filename: "UPDATE_ALL_FILES", + receiver_flo_address: update_script_request.user_flo_address, }); doSend(response_from_sever); } @@ -40270,9 +14730,25 @@ case "addNewKbucketNode": try { - const newKbucketObject = res_obj.params[0]; - localbitcoinplusplus.kademlia.addNewUserNodeInKbucket("FLO_TEST", - newKbucketObject.newKbucketNode.id, newKbucketObject.newKbucketNode.data); + localbitcoinplusplus.kademlia.determineClosestSupernode(res_obj.globalParams.senderFloId) + .then(my_closest_su=>{ + if (localbitcoinplusplus.wallets.my_local_flo_address !== my_closest_su[0].data.id) return; + + const newKbucketObjectObj = res_obj.params[0]; + if (typeof newKbucketObjectObj.newKbucketNode == "object") { + newKbucketObject = newKbucketObjectObj.newKbucketNode; + + newKbucketObject_id_array = Object.values(newKbucketObject.id); + newKbucketObject_idu8 = new Uint8Array(newKbucketObject_id_array); + localbitcoinplusplus.kademlia.addNewUserNodeInKbucketAndDB("FLO_TEST", + newKbucketObject_idu8, newKbucketObject.data); + } else { + let mss = `WARNING: Failed to add ${res_obj.globalParams.senderFloId} to KBucket.`; + showMessage(mss) + console.warn(mss); + } + }); + } catch (error) { console.error(error); } @@ -40294,11 +14770,97 @@ } 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" + ]; + 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; + + // 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 + 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 { + 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]); + } + } + } + } + + } catch (error) { + console.log(error); + } + } + })(); + + } + break; + case "messageBroadcasting": console.log(res_obj); try { - let data = res_obj.params[0]; - let msg = localbitcoinplusplus.encrypt.decryptMessage(data.secret, data.senderPublicKeyString); + 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); @@ -40306,6 +14868,14 @@ 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; @@ -40326,10 +14896,120 @@ } function doSend(message) { - writeToScreen("SENT: " + message); - websocket.send(message); + + const request_array = ['send_back_shamirs_secret_supernode_pvtkey', + 'retrieve_shamirs_secret_supernode_pvtkey', + 'store_shamirs_secret_pvtkey_shares']; + + let finalMessage = message; + + const msgObj = JSON.parse(message); + + if (!request_array.includes(msgObj.method)) { + const RM_WALLET = new localbitcoinplusplus.wallets; + + if (typeof message !== "string") { + message = JSON.stringify(message); + } + const message256hash = Crypto.SHA256(message); + + if(typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY !== "string") + throw new Error(`Private key could not be found.`); + + const nodeSignedMessage = RM_WALLET.sign(message256hash, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY); + + msgObj.nodeMessage256hash = message256hash; + msgObj.nodeSignedMessage = nodeSignedMessage; + msgObj.nodePubKey = localbitcoinplusplus.wallets.my_local_flo_public_key; + + finalMessage = JSON.stringify(msgObj); + } + + writeToScreen("SENT: " + finalMessage); + websocket.send(finalMessage); } + function validateIncomingMessage(message) { + return new Promise((resolve, reject)=>{ + if(message.length <1) { + showMessage(`WARNING: The incoming websocket message on was empty.`); + reject(false)}; + const request_array = ['send_back_shamirs_secret_supernode_pvtkey', + 'retrieve_shamirs_secret_supernode_pvtkey', + 'store_shamirs_secret_pvtkey_shares']; + + try { + const msgObj = JSON.parse(message); + + if (request_array.includes(msgObj.method)) return resolve(true); + + const getFloId = bitjs.FLO_TEST.pubkey2address(msgObj.nodePubKey); + + // Check if the public key belongs to real sender + if (getFloId !== msgObj.globalParams.senderFloId) { + showMessage(`Sender FLO address did not match signer FLO address.`); + reject(false) + } + const initialMsgObj = { + jsonrpc:msgObj.jsonrpc, + id:msgObj.id, + method:msgObj.method, + params:msgObj.params, + globalParams:msgObj.globalParams, + } + + const initialMsgObjStr = JSON.stringify(initialMsgObj); + console.log(initialMsgObjStr); + + const initialMsgObjStrHash = Crypto.SHA256(initialMsgObjStr); + console.log(initialMsgObjStrHash); + + const RM_WALLET = new localbitcoinplusplus.wallets; + if (RM_WALLET.verify(initialMsgObjStrHash, msgObj.nodeSignedMessage, msgObj.nodePubKey)) { + resolve(true); + } else { + showMessage(`WARNING: Incoming Websocket message verification failed.`) + reject(false); + } + + } catch (error) { + reject(error); + } + }) + } + + // function doSendToBackupServers(message) { + // try { + // const supernodesFloAddrList = localbitcoinplusplus.master_configurations.supernodesPubKeys + // .map(fid=>bitjs.FLO_TEST.pubkey2address(fid)); + + // const messageObject = JSON.parse(message) + + // if(!supernodesFloAddrList.includes(messageObject.globalParams.senderFloId)) return; + + // if(typeof localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY !== "string") + // throw new Error(`Private key could not be found.`); + + // const RM_WALLET = localbitcoinplusplus.wallets; + + // const supernodeSignedMessage = RM_WALLET.sign(message, localbitcoinplusplus.wallets.MY_SUPERNODE_PRIVATE_KEY); + + // messageObject.supernodeSignedMessage = supernodeSignedMessage; + // messageObject.supernodePubKey = localbitcoinplusplus.wallets.my_local_flo_public_key; + // messageObject.messageForOnlySupernodes = true; + // messageObject.method = `backup_${messageObject.method}`; + + // const messageString = JSON.stringify(messageObject); + + // supernodesFloAddrList.map(flo_id=>localbitcoinplusplus.encrypt + // .messageBroadcasting(messageString, flo_id, messageObject.method)); + + // } catch (error) { + // console.error(error); + // } + + // } + function writeToScreen(message) { // var pre = document.createElement("p"); // pre.style.wordWrap = "break-word"; @@ -40448,6 +15128,7 @@ const kBucketStore = { id: null, + vectorClock: 0, data: null, last_updated_on: null, } @@ -40462,7 +15143,7 @@ var db; const DBName = "localbitcoinDB"; - var request = window.indexedDB.open(DBName, 1); + const request = window.indexedDB.open(DBName, 1); request.onerror = function (event) { //https://stackoverflow.com/questions/13972385/invalidstateerror-while-opening-indexeddb-in-firefox @@ -40473,6 +15154,7 @@ request.onsuccess = function (event) { db = request.result; + loadLocalDBData(); }; request.onupgradeneeded = function (event) { @@ -40621,7 +15303,17 @@ unique: false }); } - + if (!db.objectStoreNames.contains('myClosestSupernodes')) { + var objectStore = db.createObjectStore("myClosestSupernodes", { + keyPath: 'id' + }); + objectStore.createIndex('trader_flo_address', 'trader_flo_address', { + unique: true + }); + objectStore.createIndex('ip', 'ip', { + unique: false + }); + } } function readDB(tablename, id) { @@ -40755,6 +15447,319 @@ } + + + @@ -41258,7 +16300,7 @@ 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", null); + "refresh_deposit_status_request", {receiver_flo_address: localbitcoinplusplus.MY_SUPERNODE_FLO_ADDRESS}); doSend(refresh_deposit_status); }); @@ -41411,115 +16453,7 @@ })(); - - - diff --git a/supernode/websocket_chat b/supernode/websocket_chat index c882df3..a51368c 100755 Binary files a/supernode/websocket_chat and b/supernode/websocket_chat differ diff --git a/supernode/websocket_chat.c b/supernode/websocket_chat.c index d0c42b8..084e547 100644 --- a/supernode/websocket_chat.c +++ b/supernode/websocket_chat.c @@ -6,7 +6,7 @@ #include "mongoose.h" static sig_atomic_t s_signal_received = 0; -static const char *s_http_port = "9000"; +static const char *s_http_port = "9001"; static struct mg_serve_http_opts s_http_server_opts; static void signal_handler(int sig_num) {