diff --git a/index.html b/index.html index f61fc65..6ebe909 100644 --- a/index.html +++ b/index.html @@ -13434,7 +13434,7 @@ Bitcoin.Util = { var floID = key.getBitcoinAddress(); return floID; } catch (e) { - console.error(e); + return null; } }, @@ -13559,22 +13559,24 @@ Bitcoin.Util = { }, //Write Data into blockchain - writeData: function (senderAddr, Data, PrivKey, receiverAddr = floGlobals.adminID) { + writeData: function (senderAddr, data, privKey, receiverAddr = floGlobals.adminID) { return new Promise((resolve, reject) => { - this.sendTx(senderAddr, receiverAddr, floGlobals.sendAmt, PrivKey, Data) + if (typeof data != "string") + data = JSON.stringify(data); + this.sendTx(senderAddr, receiverAddr, floGlobals.sendAmt, privKey, data) .then(txid => resolve(txid)) .catch(error => reject(error)) }); }, //Send Tx to blockchain - sendTx: function (senderAddr, receiverAddr, sendAmt, PrivKey, floData = '') { + sendTx: function (senderAddr, receiverAddr, sendAmt, privKey, floData = '') { return new Promise((resolve, reject) => { if (!floCrypto.validateAddr(senderAddr)) reject(`Invalid address : ${senderAddr}`); else if (!floCrypto.validateAddr(receiverAddr)) reject(`Invalid address : ${receiverAddr}`); - if (PrivKey.length < 1 || !floCrypto.verifyPrivKey(PrivKey, senderAddr)) + if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr)) reject("Invalid Private key!"); else if (typeof sendAmt !== 'number' || sendAmt <= 0) reject(`Invalid sendAmt : ${sendAmt}`); @@ -13598,8 +13600,8 @@ Bitcoin.Util = { var change = utxoAmt - sendAmt - fee; if (change > 0) trx.addoutput(senderAddr, change); - trx.addflodata(floData); - var signedTxHash = trx.sign(PrivKey, 1); + trx.addflodata(floData.replace(/\n/g, ' ')); + var signedTxHash = trx.sign(privKey, 1); this.broadcastTx(signedTxHash) .then(txid => resolve(txid)) .catch(error => reject(error)) @@ -13609,11 +13611,239 @@ Bitcoin.Util = { }); }, + //merge all UTXOs of a given floID into a single UTXO + mergeUTXOs: function (floID, privKey, floData = '') { + return new Promise((resolve, reject) => { + if (!floCrypto.validateAddr(floID)) + return reject(`Invalid floID`); + if (!floCrypto.verifyPrivKey(privKey, floID)) + return reject("Invalid Private Key") + + var trx = bitjs.transaction(); + var utxoAmt = 0.0; + var fee = floGlobals.fee; + this.promisedAPI(`api/addr/${floID}/utxo`).then(utxos => { + for (var i = utxos.length - 1; i >= 0; i--) { + if (utxos[i].confirmations) { + trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i] + .scriptPubKey) + utxoAmt += utxos[i].amount; + } + } + trx.addoutput(floID, utxoAmt - fee); + trx.addflodata(floData.replace(/\n/g, ' ')); + var signedTxHash = trx.sign(privKey, 1); + this.broadcastTx(signedTxHash) + .then(txid => resolve(txid)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }) + }, + + /**Write data into blockchain from (and/or) to multiple floID + * @param {Array} senderPrivKeys List of sender private-keys + * @param {string} data FLO data of the txn + * @param {Array} receivers List of receivers + * @param {boolean} preserveRatio (optional) preserve ratio or equal contribution + * @return {Promise} + */ + writeDataMultiple: function (senderPrivKeys, data, receivers = [floGlobals.adminID], preserveRatio = true) { + return new Promise((resolve, reject) => { + if (!Array.isArray(senderPrivKeys)) + return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array") + if (!preserveRatio) { + let tmp = {}; + let amount = (floGlobals.sendAmt * receivers.length) / senderPrivKeys.length; + senderPrivKeys.forEach(key => tmp[key] = amount); + senderPrivKeys = tmp + } + if (!Array.isArray(receivers)) + return reject("Invalid receivers: Receivers must be Array") + else { + let tmp = {}; + let amount = floGlobals.sendAmt; + receivers.forEach(floID => tmp[floID] = amount); + receivers = tmp + } + if (typeof data != "string") + data = JSON.stringify(data); + this.sendTxMultiple(senderPrivKeys, receivers, data) + .then(txid => resolve(txid)) + .catch(error => reject(error)) + }) + }, + + /**Send Tx from (and/or) to multiple floID + * @param {Array or Object} senderPrivKeys List of sender private-key (optional: with coins to be sent) + * @param {Object} receivers List of receivers with respective amount to be sent + * @param {string} floData FLO data of the txn + * @return {Promise} + */ + sendTxMultiple: function (senderPrivKeys, receivers, floData = '') { + return new Promise((resolve, reject) => { + + let senders = {}, + preserveRatio; + //check for argument validations + try { + let invalids = { + InvalidSenderPrivKeys: [], + InvalidSenderAmountFor: [], + InvalidReceiverIDs: [], + InvalidReceiveAmountFor: [] + } + let inputVal = 0, + outputVal = 0; + //Validate sender privatekeys (and send amount if passed) + //conversion when only privateKeys are passed (preserveRatio mode) + if (Array.isArray(senderPrivKeys)) { + senderPrivKeys.forEach(key => { + try { + if (!key) + invalids.InvalidSenderPrivKeys.push(key); + else { + let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto + .getPubKeyHex(key)); + senders[floID] = { + wif: key + } + } + } catch (error) { + invalids.InvalidSenderPrivKeys.push(key) + } + }) + preserveRatio = true; + } + //conversion when privatekeys are passed with send amount + else { + for (let key in senderPrivKeys) { + try { + if (!key) + invalids.InvalidSenderPrivKeys.push(key); + else { + if (typeof senderPrivKeys[key] !== 'number' || senderPrivKeys[ + key] <= 0) + invalids.InvalidSenderAmountFor.push(key) + else + inputVal += senderPrivKeys[key]; + let floID = floCrypto.getFloIDfromPubkeyHex(floCrypto.getPubKeyHex( + key)); + senders[floID] = { + wif: key, + coins: senderPrivKeys[key] + } + } + } catch (error) { + invalids.InvalidSenderPrivKeys.push(key) + } + } + preserveRatio = false; + } + //Validate the receiver IDs and receive amount + for (let floID in receivers) { + if (!floCrypto.validateAddr(floID)) + invalids.InvalidReceiverIDs.push(floID) + if (typeof receivers[floID] !== 'number' || receivers[floID] <= 0) + invalids.InvalidReceiveAmountFor.push(floID) + else + outputVal += receivers[floID]; + } + //Reject if any invalids are found + for (let i in invalids) + if (!invalids[i].length) + delete invalids[i]; + if (Object.keys(invalids).length) + return reject(invalids); + //Reject if given inputVal and outputVal are not equal + if (!preserveRatio && inputVal != outputVal) + return reject( + `Input Amount (${inputVal}) not equal to Output Amount (${outputVal})`) + } catch (error) { + return reject(error) + } + //Get balance of senders + let promises = [] + for (let floID in senders) + promises.push(this.getBalance(floID)) + Promise.all(promises).then(results => { + let totalBalance = 0, + totalFee = floGlobals.fee, + balance = {}; + //Divide fee among sender if not for preserveRatio + if (!preserveRatio) + var dividedFee = totalFee / Object.keys(senders).length; + //Check if balance of each sender is sufficient enough + let insufficient = []; + for (let floID in senders) { + balance[floID] = parseFloat(results.shift()); + if (isNaN(balance[floID]) || (preserveRatio && balance[floID] <= + totalFee) || (!preserveRatio && balance[floID] < senders[floID] + .coins + dividedFee)) + insufficient.push(floID) + totalBalance += balance[floID]; + } + if (insufficient.length) + return reject({ + InsufficientBalance: insufficient + }) + //Calculate totalSentAmount and check if totalBalance is sufficient + let totalSendAmt = totalFee; + for (floID in receivers) + totalSendAmt += receivers[floID]; + if (totalBalance < totalSendAmt) + return reject("Insufficient total Balance") + //Get the UTXOs of the senders + let promises = [] + for (floID in senders) + promises.push(this.promisedAPI(`api/addr/${floID}/utxo`)) + Promise.all(promises).then(results => { + let wifSeq = []; + var trx = bitjs.transaction(); + for (floID in senders) { + let utxos = results.shift(); + let sendAmt; + if (preserveRatio) { + let ratio = (balance[floID] / totalBalance); + sendAmt = totalSendAmt * ratio; + } else + sendAmt = senders[floID].coins + dividedFee; + let wif = senders[floID].wif; + let utxoAmt = 0.0; + for (let i = utxos.length - 1; + (i >= 0) && (utxoAmt < sendAmt); i--) { + if (utxos[i].confirmations) { + trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i] + .scriptPubKey) + wifSeq.push(wif); + utxoAmt += utxos[i].amount; + } + } + if (utxoAmt < sendAmt) + return reject("Insufficient balance:" + floID); + let change = (utxoAmt - sendAmt); + if (change > 0) + trx.addoutput(floID, change); + } + for (floID in receivers) + trx.addoutput(floID, receivers[floID]); + trx.addflodata(floData.replace(/\n/g, ' ')); + for (let i = 0; i < wifSeq.length; i++) + trx.signinput(i, wifSeq[i], 1); + var signedTxHash = trx.serialize(); + this.broadcastTx(signedTxHash) + .then(txid => resolve(txid)) + .catch(error => reject(error)) + }).catch(error => reject(error)) + }).catch(error => reject(error)) + }) + }, + //Broadcast signed Tx in blockchain using API broadcastTx: function (signedTxHash) { return new Promise((resolve, reject) => { var request = new XMLHttpRequest(); var url = this.util.serverList[this.util.curPos] + 'api/tx/send'; + console.log(url) if (signedTxHash.length < 1) reject("Empty Signature"); else { @@ -13663,7 +13893,7 @@ Bitcoin.Util = { filter : custom filter funtion for floData (eg . filter: d => {return d[0] == '$'}) */ readData: function (addr, options = {}) { - options.limit = options.limit | 1000 + options.limit = options.limit | 0 options.ignoreOld = options.ignoreOld | 0 return new Promise((resolve, reject) => { this.promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { @@ -13678,13 +13908,17 @@ Bitcoin.Util = { if (options.sentOnly && response.items[i].vin[0].addr !== addr) continue; - if (options.pattern && !response.items[i].floData - .startsWith(options.pattern, 0) && !response.items[i] - .floData.startsWith(options.pattern, 2)) - continue; - if (options.contains && !response.items[i].floData.includes( - options.contains)) - continue; + if (options.pattern) { + try { + let jsonContent = JSON.parse(response.items[i] + .floData) + if (!Object.keys(jsonContent).includes(options + .pattern)) + continue; + } catch (error) { + continue; + } + } if (options.filter && !options.filter(response.items[i] .floData)) continue; @@ -14354,12 +14588,12 @@ Bitcoin.Util = { const compactIDB = { setDefaultDB: function (dbName) { - this.dbName = dbName; + this.defaultDB = dbName; }, initDB: function (dbName, objectStores = {}, version = null, removeStores = []) { return new Promise((resolve, reject) => { - this.dbName = this.dbName || dbName; + this.defaultDB = this.defaultDB || dbName; var idb = version ? indexedDB.open(dbName, version) : indexedDB.open(dbName); idb.onerror = (event) => { reject("Error in opening IndexedDB!"); @@ -14399,7 +14633,7 @@ Bitcoin.Util = { }); }, - openDB: function (dbName = this.dbName) { + openDB: function (dbName = this.defaultDB) { return new Promise((resolve, reject) => { var idb = indexedDB.open(dbName); idb.onerror = (event) => reject("Error in opening IndexedDB!"); @@ -14407,7 +14641,7 @@ Bitcoin.Util = { }); }, - deleteDB: function (dbName = this.dbName) { + deleteDB: function (dbName = this.defaultDB) { return new Promise((resolve, reject) => { var deleteReq = indexedDB.deleteDatabase(dbName);; deleteReq.onerror = (event) => reject("Error deleting database!"); @@ -14415,7 +14649,7 @@ Bitcoin.Util = { }); }, - writeData: function (obsName, data, key = false, dbName = this.dbName) { + writeData: function (obsName, data, key = false, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -14429,7 +14663,7 @@ Bitcoin.Util = { }); }, - addData: function (obsName, data, key = false, dbName = this.dbName) { + addData: function (obsName, data, key = false, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -14443,7 +14677,7 @@ Bitcoin.Util = { }); }, - removeData: function (obsName, key, dbName = this.dbName) { + removeData: function (obsName, key, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -14457,7 +14691,7 @@ Bitcoin.Util = { }); }, - clearData: function (obsName, dbName = this.dbName) { + clearData: function (obsName, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readwrite").objectStore(obsName); @@ -14469,7 +14703,7 @@ Bitcoin.Util = { }); }, - readData: function (obsName, key, dbName = this.dbName) { + readData: function (obsName, key, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readonly").objectStore(obsName); @@ -14483,7 +14717,7 @@ Bitcoin.Util = { }); }, - readAllData: function (obsName, dbName = this.dbName) { + readAllData: function (obsName, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readonly").objectStore(obsName); @@ -14505,7 +14739,7 @@ Bitcoin.Util = { }); }, - searchData: function (obsName, patternEval, dbName = this.dbName) { + searchData: function (obsName, patternEval, dbName = this.defaultDB) { return new Promise((resolve, reject) => { this.openDB(dbName).then(db => { var obs = db.transaction(obsName, "readonly").objectStore(obsName); @@ -14683,16 +14917,15 @@ Bitcoin.Util = { } else { var privKey; inputFn("PRIVATE_KEY").then(result => { - try { - if (!result) - return reject("Empty Private Key") - var floID = floCrypto.getFloIDfromPubkeyHex( - floCrypto.getPubKeyHex(result)) - privKey = result - } catch (error) { - console.error(error) + if (!result) + return reject("Empty Private Key") + var pubKey = floCrypto.getPubKeyHex(result) + if(!pubKey) return reject("Invalid Private Key") - } + var floID = floCrypto.getFloIDfromPubkeyHex(pubKey) + if(!floID || !floCrypto.validateAddr(floID)) + return reject("Invalid Private Key") + privKey = result; }).catch(error => { console.log(error, "Generating Random Keys") privKey = floCrypto.generateNewID().privKey