From 1e9c414bad6e15de6ebebd90e1c41881e28d9e2c Mon Sep 17 00:00:00 2001 From: sairajzero Date: Fri, 18 Nov 2022 18:23:51 +0530 Subject: [PATCH] Update stdops - Added option parameter to multisig.createTx in messenger module. option values {} for btcOperator.createMultiSigTx {fee_from_receiver, change_address} --- scripts/btcOperator.js | 330 ++++++++++++++++++++++++++++-------- scripts/floBlockchainAPI.js | 45 ++--- scripts/floCloudAPI.js | 88 +++++----- scripts/floCrypto.js | 62 ++++--- scripts/messenger.js | 129 +++++++------- 5 files changed, 419 insertions(+), 235 deletions(-) diff --git a/scripts/btcOperator.js b/scripts/btcOperator.js index cf3d316..85ae761 100644 --- a/scripts/btcOperator.js +++ b/scripts/btcOperator.js @@ -1,4 +1,4 @@ -(function (EXPORTS) { //btcOperator v1.0.8 +(function (EXPORTS) { //btcOperator v1.0.13b /* BTC Crypto and API Operator */ const btcOperator = EXPORTS; @@ -16,14 +16,14 @@ }) }; - const SIGN_SIZE = 73; + const SATOSHI_IN_BTC = 1e8; function get_fee_rate() { return new Promise((resolve, reject) => { fetch('https://api.blockchain.info/mempool/fees').then(response => { if (response.ok) response.json() - .then(result => resolve(result.regular)) + .then(result => resolve(parseFloat((result.regular / SATOSHI_IN_BTC).toFixed(8)))) .catch(error => reject(error)); else reject(response); @@ -31,17 +31,31 @@ }) } - const broadcast = btcOperator.broadcast = rawtx => new Promise((resolve, reject) => { - $.ajax({ - type: "POST", - url: URL + "send_tx/BTC/", - data: { - "tx_hex": rawtx + const broadcastTx = btcOperator.broadcastTx = rawTxHex => new Promise((resolve, reject) => { + let url = 'https://coinb.in/api/?uid=1&key=12345678901234567890123456789012&setmodule=bitcoin&request=sendrawtransaction'; + fetch(url, { + method: 'POST', + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' }, - dataType: "json", - error: e => reject(e.responseJSON), - success: r => r.status === "success" ? resolve(r.data) : reject(r) - }) + body: "rawtx=" + rawTxHex + }).then(response => { + response.text().then(resultText => { + let r = resultText.match(/.*<\/result>/); + if (!r) + reject(resultText); + else { + r = r.pop().replace('', '').replace('', ''); + if (r == '1') { + let txid = resultText.match(/.*<\/txid>/).pop().replace('', '').replace('', ''); + resolve(txid); + } else if (r == '0') { + let error = resultText.match(/.*<\/response>/).pop().replace('', '').replace('', ''); + reject(decodeURIComponent(error.replace(/\+/g, " "))); + } else reject(resultText); + } + }).catch(error => reject(error)) + }).catch(error => reject(error)) }); Object.defineProperties(btcOperator, { @@ -204,6 +218,17 @@ .catch(error => reject(error)) }); + const BASE_TX_SIZE = 12, + BASE_INPUT_SIZE = 41, + LEGACY_INPUT_SIZE = 107, + BECH32_INPUT_SIZE = 27, + SEGWIT_INPUT_SIZE = 59, + MULTISIG_INPUT_SIZE_ES = 351, + BASE_OUTPUT_SIZE = 9, + LEGACY_OUTPUT_SIZE = 25, + BECH32_OUTPUT_SIZE = 23, + SEGWIT_OUTPUT_SIZE = 23; + function _redeemScript(addr, key) { let decode = coinjs.addressDecode(addr); switch (decode.type) { @@ -218,6 +243,39 @@ } } + function _sizePerInput(addr, rs) { + switch (coinjs.addressDecode(addr).type) { + case "standard": + return BASE_INPUT_SIZE + LEGACY_INPUT_SIZE; + case "bech32": + return BASE_INPUT_SIZE + BECH32_INPUT_SIZE; + case "multisig": + switch (coinjs.script().decodeRedeemScript(rs).type) { + case "segwit__": + return BASE_INPUT_SIZE + SEGWIT_INPUT_SIZE; + case "multisig__": + return BASE_INPUT_SIZE + MULTISIG_INPUT_SIZE_ES; + default: + return null; + }; + default: + return null; + } + } + + function _sizePerOutput(addr) { + switch (coinjs.addressDecode(addr).type) { + case "standard": + return BASE_OUTPUT_SIZE + LEGACY_OUTPUT_SIZE; + case "bech32": + return BASE_OUTPUT_SIZE + BECH32_OUTPUT_SIZE; + case "multisig": + return BASE_OUTPUT_SIZE + SEGWIT_OUTPUT_SIZE; + default: + return null; + } + } + function validateTxParameters(parameters) { let invalids = []; //sender-ids @@ -249,8 +307,8 @@ parameters.receivers.forEach(id => !validateAddress(id) ? invalids.push(id) : null); if (invalids.length) throw "Invalid receivers:" + invalids; - if (parameters.change_addr && !validateAddress(parameters.change_addr)) - throw "Invalid change_address:" + parameters.change_addr; + if (parameters.change_address && !validateAddress(parameters.change_address)) + throw "Invalid change_address:" + parameters.change_address; //fee and amounts if ((typeof parameters.fee !== "number" || parameters.fee <= 0) && parameters.fee !== null) //fee = null (auto calc) throw "Invalid fee:" + parameters.fee; @@ -265,48 +323,88 @@ return parameters; } - const TMP_FEE = 0.00001; - - function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr) { + function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_address, fee_from_receiver) { return new Promise((resolve, reject) => { - let auto_fee = false, - total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8)); - if (fee === null) { - auto_fee = true; - fee = TMP_FEE; - } + let total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8)); const tx = coinjs.transaction(); - addUTXOs(tx, senders, redeemScripts, total_amount + fee).then(result => { - if (result > 0) - return reject("Insufficient Balance"); - let change = addOutputs(tx, receivers, amounts, Math.abs(result), change_addr); - if (!auto_fee) - return resolve(tx); - autoFeeCalc(tx).then(fee_calc => { - fee = Math.round((fee * 1) * 1e8); //satoshi convertion - if (!change) - tx.addoutput(change_addr, 0); - editFee(tx, fee, fee_calc); - resolve(tx); - }).catch(error => reject(error)) - }) + let output_size = addOutputs(tx, receivers, amounts, change_address); + addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver).then(result => { + if (result.change_amount > 0) //add change amount if any + tx.outs[tx.outs.length - 1].value = parseInt(result.change_amount * SATOSHI_IN_BTC); //values are in satoshi + if (fee_from_receiver) { //deduce fee from receivers if fee_from_receiver + let fee_remaining = parseInt(result.fee * SATOSHI_IN_BTC); + for (let i = 0; i < tx.outs.length - 1 && fee_remaining > 0; i++) { + if (fee_remaining < tx.outs[i].value) { + tx.outs[i].value -= fee_remaining; + fee_remaining = 0; + } else { + fee_remaining -= tx.outs[i].value; + tx.outs[i].value = 0; + } + } + if (fee_remaining > 0) + return reject("Send amount is less than fee"); + + } + tx.outs = tx.outs.filter(o => o.value !== 0); //remove all output with value 0 + result.output_size = output_size; + result.output_amount = total_amount - (fee_from_receiver ? result.fee : 0); + result.total_size = BASE_TX_SIZE + output_size + result.input_size; + result.transaction = tx; + resolve(result); + }).catch(error => reject(error)) }) } - function addUTXOs(tx, senders, redeemScripts, required_amount, n = 0) { + function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver) { + return new Promise((resolve, reject) => { + if (fee !== null) { + addUTXOs(tx, senders, redeemScripts, fee_from_receiver ? total_amount : total_amount + fee, false).then(result => { + result.fee = fee; + resolve(result); + }).catch(error => reject(error)) + } else { + get_fee_rate().then(fee_rate => { + let net_fee = BASE_TX_SIZE * fee_rate; + net_fee += (output_size * fee_rate); + (fee_from_receiver ? + addUTXOs(tx, senders, redeemScripts, total_amount, false) : + addUTXOs(tx, senders, redeemScripts, total_amount + net_fee, fee_rate) + ).then(result => { + result.fee = parseFloat((net_fee + (result.input_size * fee_rate)).toFixed(8)); + result.fee_rate = fee_rate; + resolve(result); + }).catch(error => reject(error)) + }).catch(error => reject(error)) + } + }) + } + + function addUTXOs(tx, senders, redeemScripts, required_amount, fee_rate, rec_args = {}) { return new Promise((resolve, reject) => { required_amount = parseFloat(required_amount.toFixed(8)); - if (required_amount <= 0 || n >= senders.length) - return resolve(required_amount); - let addr = senders[n], - rs = redeemScripts[n]; + if (typeof rec_args.n === "undefined") { + rec_args.n = 0; + rec_args.input_size = 0; + rec_args.input_amount = 0; + } + if (required_amount <= 0) + return resolve({ + input_size: rec_args.input_size, + input_amount: rec_args.input_amount, + change_amount: required_amount * -1 //required_amount will be -ve of change_amount + }); + else if (rec_args.n >= senders.length) + return reject("Insufficient Balance"); + let addr = senders[rec_args.n], + rs = redeemScripts[rec_args.n]; + let size_per_input = _sizePerInput(addr, rs); fetch_api(`get_tx_unspent/BTC/${addr}`).then(result => { let utxos = result.data.txs; console.debug("add-utxo", addr, rs, required_amount, utxos); for (let i = 0; i < utxos.length && required_amount > 0; i++) { if (!utxos[i].confirmations) //ignore unconfirmed utxo continue; - required_amount -= parseFloat(utxos[i].value); var script; if (!rs || !rs.length) //legacy script script = utxos[i].script_hex; @@ -315,29 +413,38 @@ let s = coinjs.script(); s.writeBytes(Crypto.util.hexToBytes(rs)); s.writeOp(0); - s.writeBytes(coinjs.numToBytes((utxos[i].value * 100000000).toFixed(0), 8)); + s.writeBytes(coinjs.numToBytes((utxos[i].value * SATOSHI_IN_BTC).toFixed(0), 8)); script = Crypto.util.bytesToHex(s.buffer); } else //redeemScript for multisig script = rs; tx.addinput(utxos[i].txid, utxos[i].output_no, script, 0xfffffffd /*sequence*/); //0xfffffffd for Replace-by-fee + //update track values + rec_args.input_size += size_per_input; + rec_args.input_amount += parseFloat(utxos[i].value); + required_amount -= parseFloat(utxos[i].value); + if (fee_rate) //automatic fee calculation (dynamic) + required_amount += size_per_input * fee_rate; } - addUTXOs(tx, senders, redeemScripts, required_amount, n + 1) + rec_args.n += 1; + addUTXOs(tx, senders, redeemScripts, required_amount, fee_rate, rec_args) .then(result => resolve(result)) .catch(error => reject(error)) }).catch(error => reject(error)) }) } - function addOutputs(tx, receivers, amounts, change, change_addr) { - for (let i in receivers) + function addOutputs(tx, receivers, amounts, change_address) { + let size = 0; + for (let i in receivers) { tx.addoutput(receivers[i], amounts[i]); - if (parseFloat(change.toFixed(8)) > 0) { - tx.addoutput(change_addr, change); - return true; - } else - return false; + size += _sizePerOutput(receivers[i]); + } + tx.addoutput(change_address, 0); + size += _sizePerOutput(change_address); + return size; } + /* function autoFeeCalc(tx) { return new Promise((resolve, reject) => { get_fee_rate().then(fee_rate => { @@ -362,18 +469,30 @@ function editFee(tx, current_fee, target_fee, index = -1) { //values are in satoshi - index = parseInt(index >= 0 ? index : tx.out.length - index); - if (index < 0 || index >= tx.out.length) + index = parseInt(index >= 0 ? index : tx.outs.length - index); + if (index < 0 || index >= tx.outs.length) throw "Invalid index"; let edit_value = parseInt(current_fee - target_fee), //rip of any decimal places - current_value = tx.out[index].value; //could be BigInterger + current_value = tx.outs[index].value; //could be BigInterger if (edit_value < 0 && edit_value > current_value) throw "Insufficient value at vout"; - tx.out[index].value = current_value instanceof BigInteger ? + tx.outs[index].value = current_value instanceof BigInteger ? current_value.add(new BigInteger('' + edit_value)) : parseInt(current_value + edit_value); } + */ - btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee, change_addr = null) { + btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) { + return new Promise((resolve, reject) => { + createSignedTx(senders, privkeys, receivers, amounts, fee, options).then(result => { + debugger; + broadcastTx(result.transaction.serialize()) + .then(txid => resolve(txid)) + .catch(error => reject(error)); + }).catch(error => reject(error)) + }) + } + + const createSignedTx = btcOperator.createSignedTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) { return new Promise((resolve, reject) => { try { ({ @@ -387,7 +506,7 @@ receivers, amounts, fee, - change_addr + change_address: options.change_address })); } catch (e) { return reject(e) @@ -402,19 +521,17 @@ if (redeemScripts.includes(null)) //TODO: segwit return reject("Unable to get redeem-script"); //create transaction - createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr || senders[0]).then(tx => { + createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => { + let tx = result.transaction; console.debug("Unsigned:", tx.serialize()); new Set(wif_keys).forEach(key => console.debug("Signing key:", key, tx.sign(key, 1 /*sighashtype*/))); //Sign the tx using private key WIF console.debug("Signed:", tx.serialize()); - debugger; - broadcast(tx.serialize()) - .then(result => resolve(result)) - .catch(error => reject(error)); + resolve(result); }).catch(error => reject(error)); }) } - btcOperator.createTx = function (senders, receivers, amounts, fee = null, change_addr = null) { + btcOperator.createTx = function (senders, receivers, amounts, fee = null, options = {}) { return new Promise((resolve, reject) => { try { ({ @@ -426,7 +543,7 @@ receivers, amounts, fee, - change_addr + change_address: options.change_address })); } catch (e) { return reject(e) @@ -435,13 +552,15 @@ if (redeemScripts.includes(null)) //TODO: segwit return reject("Unable to get redeem-script"); //create transaction - createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr || senders[0]) - .then(tx => resolve(tx.serialize())) - .catch(error => reject(error)) + createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => { + result.tx_hex = result.transaction.serialize(); + delete result.transaction; + resolve(result); + }).catch(error => reject(error)) }) } - btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee) { + btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee = null, options = {}) { return new Promise((resolve, reject) => { //validate tx parameters if (validateAddress(sender) !== "multisig") @@ -459,15 +578,19 @@ } = validateTxParameters({ receivers, amounts, - fee + fee, + change_address: options.change_address })); } catch (e) { return reject(e) } //create transaction - createTransaction([sender], [redeemScript], receivers, amounts, fee, sender) - .then(tx => resolve(tx.serialize())) - .catch(error => reject(error)) + createTransaction([sender], [redeemScript], receivers, amounts, fee, options.change_address || sender, options.fee_from_receiver).then(result => { + result.tx_hex = result.transaction.serialize(); + delete result.transaction; + resolve(result); + }).catch(error => reject(error)) + }) } @@ -494,7 +617,7 @@ return tx.serialize(); } - btcOperator.checkSigned = function (tx, bool = true) { + const checkSigned = btcOperator.checkSigned = function (tx, bool = true) { tx = deserializeTx(tx); let n = []; for (let i in tx.ins) { @@ -533,6 +656,63 @@ return true; } + const getTxOutput = (txid, i) => new Promise((resolve, reject) => { + fetch_api(`get_tx_outputs/BTC/${txid}/${i}`) + .then(result => resolve(result.data.outputs)) + .catch(error => reject(error)) + }); + + btcOperator.parseTransaction = function (tx) { + return new Promise((resolve, reject) => { + tx = deserializeTx(tx); + let result = {}; + let promises = []; + //Parse Inputs + for (let i = 0; i < tx.ins.length; i++) + promises.push(getTxOutput(tx.ins[i].outpoint.hash, tx.ins[i].outpoint.index)); + Promise.all(promises).then(inputs => { + result.inputs = inputs.map(inp => Object({ + address: inp.address, + value: parseFloat(inp.value) + })); + let signed = checkSigned(tx, false); + result.inputs.forEach((inp, i) => inp.signed = signed[i]); + //Parse Outputs + result.outputs = tx.outs.map(out => { + var address; + switch (out.script.chunks[0]) { + case 0: //bech32 + address = encodeBech32(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.bech32.version, coinjs.bech32.hrp); + break; + case 169: //multisig, segwit + address = encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.multisig); + break; + case 118: //legacy + address = encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[2]), coinjs.pub); + } + return { + address, + value: parseFloat(out.value / SATOSHI_IN_BTC) + } + }); + //Parse Totals + result.total_input = parseFloat(result.inputs.reduce((a, inp) => a += inp.value, 0).toFixed(8)); + result.total_output = parseFloat(result.outputs.reduce((a, out) => a += out.value, 0).toFixed(8)); + result.fee = parseFloat((result.total_input - result.total_output).toFixed(8)); + resolve(result); + }).catch(error => reject(error)) + }) + } + + btcOperator.transactionID = function (tx) { + tx = deserializeTx(tx); + let clone = coinjs.clone(tx); + clone.witness = null; + let raw_bytes = Crypto.util.hexToBytes(clone.serialize()); + let txid = Crypto.SHA256(Crypto.SHA256(raw_bytes, { asBytes: true }), { asBytes: true }).reverse(); + return Crypto.util.bytesToHex(txid); + } + btcOperator.getTx = txid => new Promise((resolve, reject) => { fetch_api(`get_tx/BTC/${txid}`) .then(result => resolve(result.data)) diff --git a/scripts/floBlockchainAPI.js b/scripts/floBlockchainAPI.js index d250f86..473b192 100644 --- a/scripts/floBlockchainAPI.js +++ b/scripts/floBlockchainAPI.js @@ -1,4 +1,4 @@ -(function(EXPORTS) { //floBlockchainAPI v2.3.3b +(function (EXPORTS) { //floBlockchainAPI v2.3.3d /* FLO Blockchain Operator to send/receive data from blockchain using API calls*/ 'use strict'; const floBlockchainAPI = EXPORTS; @@ -11,6 +11,7 @@ }, sendAmt: 0.001, fee: 0.0005, + minChangeAmt: 0.0005, receiverID: floGlobals.adminID }; @@ -102,7 +103,7 @@ }); //Promised function to get data from API - const promisedAPI = floBlockchainAPI.promisedAPI = floBlockchainAPI.fetch = function(apicall) { + const promisedAPI = floBlockchainAPI.promisedAPI = floBlockchainAPI.fetch = function (apicall) { return new Promise((resolve, reject) => { //console.log(apicall); fetch_api(apicall) @@ -112,7 +113,7 @@ } //Get balance for the given Address - const getBalance = floBlockchainAPI.getBalance = function(addr) { + const getBalance = floBlockchainAPI.getBalance = function (addr) { return new Promise((resolve, reject) => { promisedAPI(`api/addr/${addr}/balance`) .then(balance => resolve(parseFloat(balance))) @@ -121,7 +122,7 @@ } //Send Tx to blockchain - const sendTx = floBlockchainAPI.sendTx = function(senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) { + const sendTx = floBlockchainAPI.sendTx = function (senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) { return new Promise((resolve, reject) => { if (!floCrypto.validateASCII(floData)) return reject("Invalid FLO_Data: only printable ASCII characters are allowed"); @@ -171,7 +172,7 @@ else { trx.addoutput(receiverAddr, sendAmt); var change = utxoAmt - sendAmt - fee; - if (change > 0) + if (change > DEFAULT.minChangeAmt) trx.addoutput(senderAddr, change); trx.addflodata(floData.replace(/\n/g, ' ')); var signedTxHash = trx.sign(privKey, 1); @@ -187,7 +188,7 @@ } //Write Data into blockchain - floBlockchainAPI.writeData = function(senderAddr, data, privKey, receiverAddr = DEFAULT.receiverID, options = {}) { + floBlockchainAPI.writeData = function (senderAddr, data, privKey, receiverAddr = DEFAULT.receiverID, options = {}) { let strict_utxo = options.strict_utxo === false ? false : true, sendAmt = isNaN(options.sendAmt) ? DEFAULT.sendAmt : options.sendAmt; return new Promise((resolve, reject) => { @@ -200,7 +201,7 @@ } //merge all UTXOs of a given floID into a single UTXO - floBlockchainAPI.mergeUTXOs = function(floID, privKey, floData = '') { + floBlockchainAPI.mergeUTXOs = function (floID, privKey, floData = '') { return new Promise((resolve, reject) => { if (!floCrypto.validateFloID(floID)) return reject(`Invalid floID`); @@ -234,7 +235,7 @@ * @param {boolean} preserveRatio (optional) preserve ratio or equal contribution * @return {Promise} */ - floBlockchainAPI.writeDataMultiple = function(senderPrivKeys, data, receivers = [DEFAULT.receiverID], preserveRatio = true) { + floBlockchainAPI.writeDataMultiple = function (senderPrivKeys, data, receivers = [DEFAULT.receiverID], preserveRatio = true) { return new Promise((resolve, reject) => { if (!Array.isArray(senderPrivKeys)) return reject("Invalid senderPrivKeys: SenderPrivKeys must be Array"); @@ -266,7 +267,7 @@ * @param {string} floData FLO data of the txn * @return {Promise} */ - const sendTxMultiple = floBlockchainAPI.sendTxMultiple = function(senderPrivKeys, receivers, floData = '') { + const sendTxMultiple = floBlockchainAPI.sendTxMultiple = function (senderPrivKeys, receivers, floData = '') { return new Promise((resolve, reject) => { if (!floCrypto.validateASCII(floData)) return reject("Invalid FLO_Data: only printable ASCII characters are allowed"); @@ -421,7 +422,7 @@ } //Broadcast signed Tx in blockchain using API - const broadcastTx = floBlockchainAPI.broadcastTx = function(signedTxHash) { + const broadcastTx = floBlockchainAPI.broadcastTx = function (signedTxHash) { return new Promise((resolve, reject) => { if (signedTxHash.length < 1) return reject("Empty Signature"); @@ -441,7 +442,7 @@ }) } - floBlockchainAPI.getTx = function(txid) { + floBlockchainAPI.getTx = function (txid) { return new Promise((resolve, reject) => { promisedAPI(`api/tx/${txid}`) .then(response => resolve(response)) @@ -450,7 +451,7 @@ } //Read Txs of Address between from and to - const readTxs = floBlockchainAPI.readTxs = function(addr, from, to) { + const readTxs = floBlockchainAPI.readTxs = function (addr, from, to) { return new Promise((resolve, reject) => { promisedAPI(`api/addrs/${addr}/txs?from=${from}&to=${to}`) .then(response => resolve(response)) @@ -459,7 +460,7 @@ } //Read All Txs of Address (newest first) - floBlockchainAPI.readAllTxs = function(addr) { + floBlockchainAPI.readAllTxs = function (addr) { return new Promise((resolve, reject) => { promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { promisedAPI(`api/addrs/${addr}/txs?from=0&to=${response.totalItems}0`) @@ -481,15 +482,15 @@ sender : flo-id(s) of sender receiver : flo-id(s) of receiver */ - floBlockchainAPI.readData = function(addr, options = {}) { + floBlockchainAPI.readData = function (addr, options = {}) { options.limit = options.limit || 0; options.ignoreOld = options.ignoreOld || 0; - if (typeof options.sender === "string") options.sender = [options.sender]; - if (typeof options.receiver === "string") options.receiver = [options.receiver]; + if (typeof options.senders === "string") options.senders = [options.senders]; + if (typeof options.receivers === "string") options.receivers = [options.receivers]; return new Promise((resolve, reject) => { promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { var newItems = response.totalItems - options.ignoreOld; - promisedAPI(`api/addrs/${addr}/txs?from=0&to=${newItems*2}`).then(response => { + promisedAPI(`api/addrs/${addr}/txs?from=0&to=${newItems * 2}`).then(response => { if (options.limit <= 0) options.limit = response.items.length; var filteredData = []; @@ -520,10 +521,10 @@ } if (!flag) continue; } - if (Array.isArray(options.sender)) { + if (Array.isArray(options.senders)) { let flag = false; for (let vin of response.items[i].vin) - if (options.sender.includes(vin.addr)) { + if (options.senders.includes(vin.addr)) { flag = true; break; } @@ -538,10 +539,10 @@ } if (!flag) continue; } - if (Array.isArray(options.receiver)) { + if (Array.isArray(options.receivers)) { let flag = false; for (let vout of response.items[i].vout) - if (options.receiver.includes(vout.scriptPubKey.addresses[0])) { + if (options.receivers.includes(vout.scriptPubKey.addresses[0])) { flag = true; break; } @@ -555,6 +556,8 @@ d.txid = response.items[i].txid; d.time = response.items[i].time; d.blockheight = response.items[i].blockheight; + d.senders = new Set(response.items[i].vin.map(v => v.addr)); + d.receivers = new Set(response.items[i].vout.map(v => v.scriptPubKey.addresses[0])); d.data = response.items[i].floData; filteredData.push(d); } else diff --git a/scripts/floCloudAPI.js b/scripts/floCloudAPI.js index 1fafb51..921fa69 100644 --- a/scripts/floCloudAPI.js +++ b/scripts/floCloudAPI.js @@ -1,4 +1,4 @@ -(function(EXPORTS) { //floCloudAPI v2.4.2d +(function (EXPORTS) { //floCloudAPI v2.4.3 /* FLO Cloud operations to send/request application data*/ 'use strict'; const floCloudAPI = EXPORTS; @@ -79,6 +79,9 @@ get: () => generalData, set: data => generalData = data }, + generalDataset: { + value: (type, options = {}) => generalData[filterKey(type, options)] + }, lastVC: { get: () => lastVC, set: vc => lastVC = vc @@ -91,7 +94,7 @@ }); var kBucket; - const K_Bucket = floCloudAPI.K_Bucket = function(masterID, nodeList) { + const K_Bucket = floCloudAPI.K_Bucket = function (masterID, nodeList) { const decodeID = floID => { let k = bitjs.Base58.decode(floID); @@ -125,7 +128,7 @@ }); self.isNode = floID => _CO.includes(floID); - self.innerNodes = function(id1, id2) { + self.innerNodes = function (id1, id2) { if (!_CO.includes(id1) || !_CO.includes(id2)) throw Error('Given nodes are not supernode'); let iNodes = [] @@ -136,7 +139,7 @@ } return iNodes } - self.outterNodes = function(id1, id2) { + self.outterNodes = function (id1, id2) { if (!_CO.includes(id1) || !_CO.includes(id2)) throw Error('Given nodes are not supernode'); let oNodes = [] @@ -147,7 +150,7 @@ } return oNodes } - self.prevNode = function(id, N = 1) { + self.prevNode = function (id, N = 1) { let n = N || _CO.length; if (!_CO.includes(id)) throw Error('Given node is not supernode'); @@ -161,7 +164,7 @@ } return (N == 1 ? pNodes[0] : pNodes) } - self.nextNode = function(id, N = 1) { + self.nextNode = function (id, N = 1) { let n = N || _CO.length; if (!_CO.includes(id)) throw Error('Given node is not supernode'); @@ -176,7 +179,7 @@ } return (N == 1 ? nNodes[0] : nNodes) } - self.closestNode = function(id, N = 1) { + self.closestNode = function (id, N = 1) { let decodedId = decodeID(id); let n = N || _CO.length; let cNodes = _KB.closest(decodedId, n) @@ -290,8 +293,8 @@ fetch_ActiveAPI(floID, data).then(response => { if (response.ok) response.json() - .then(result => resolve(result)) - .catch(error => reject(error)) + .then(result => resolve(result)) + .catch(error => reject(error)) else response.text() .then(result => reject(response.status + ": " + result)) //Error Message from Node .catch(error => reject(error)) @@ -367,21 +370,21 @@ const util = floCloudAPI.util = {}; - const encodeMessage = util.encodeMessage = function(message) { + const encodeMessage = util.encodeMessage = function (message) { return btoa(unescape(encodeURIComponent(JSON.stringify(message)))) } - const decodeMessage = util.decodeMessage = function(message) { + const decodeMessage = util.decodeMessage = function (message) { return JSON.parse(decodeURIComponent(escape(atob(message)))) } - const filterKey = util.filterKey = function(type, options) { + const filterKey = util.filterKey = function (type, options = {}) { return type + (options.comment ? ':' + options.comment : '') + '|' + (options.group || options.receiverID || DEFAULT.adminID) + '|' + (options.application || DEFAULT.application); } - const proxyID = util.proxyID = function(address) { + const proxyID = util.proxyID = function (address) { if (!address) return; var bytes; @@ -486,7 +489,7 @@ } //set status as online for user_id - floCloudAPI.setStatus = function(options = {}) { + floCloudAPI.setStatus = function (options = {}) { return new Promise((resolve, reject) => { let callback = options.callback instanceof Function ? options.callback : DEFAULT.callback; var request = { @@ -505,7 +508,7 @@ } //request status of floID(s) in trackList - floCloudAPI.requestStatus = function(trackList, options = {}) { + floCloudAPI.requestStatus = function (trackList, options = {}) { return new Promise((resolve, reject) => { if (!Array.isArray(trackList)) trackList = [trackList]; @@ -522,7 +525,7 @@ } //send any message to supernode cloud storage - const sendApplicationData = floCloudAPI.sendApplicationData = function(message, type, options = {}) { + const sendApplicationData = floCloudAPI.sendApplicationData = function (message, type, options = {}) { return new Promise((resolve, reject) => { var data = { senderID: user.id, @@ -544,7 +547,7 @@ } //request any data from supernode cloud - const requestApplicationData = floCloudAPI.requestApplicationData = function(type, options = {}) { + const requestApplicationData = floCloudAPI.requestApplicationData = function (type, options = {}) { return new Promise((resolve, reject) => { var request = { receiverID: options.receiverID || DEFAULT.adminID, @@ -647,7 +650,7 @@ */ //tag data in supernode cloud (subAdmin access only) - floCloudAPI.tagApplicationData = function(vectorClock, tag, options = {}) { + floCloudAPI.tagApplicationData = function (vectorClock, tag, options = {}) { return new Promise((resolve, reject) => { if (!floGlobals.subAdmins.includes(user.id)) return reject("Only subAdmins can tag data") @@ -668,7 +671,7 @@ } //note data in supernode cloud (receiver only or subAdmin allowed if receiver is adminID) - floCloudAPI.noteApplicationData = function(vectorClock, note, options = {}) { + floCloudAPI.noteApplicationData = function (vectorClock, note, options = {}) { return new Promise((resolve, reject) => { var request = { receiverID: options.receiverID || DEFAULT.adminID, @@ -687,7 +690,7 @@ } //send general data - floCloudAPI.sendGeneralData = function(message, type, options = {}) { + floCloudAPI.sendGeneralData = function (message, type, options = {}) { return new Promise((resolve, reject) => { if (options.encrypt) { let encryptionKey = options.encrypt === true ? @@ -701,7 +704,7 @@ } //request general data - floCloudAPI.requestGeneralData = function(type, options = {}) { + floCloudAPI.requestGeneralData = function (type, options = {}) { return new Promise((resolve, reject) => { var fk = filterKey(type, options) lastVC[fk] = parseInt(lastVC[fk]) || 0; @@ -725,7 +728,7 @@ } //request an object data from supernode cloud - floCloudAPI.requestObjectData = function(objectName, options = {}) { + floCloudAPI.requestObjectData = function (objectName, options = {}) { return new Promise((resolve, reject) => { options.lowerVectorClock = options.lowerVectorClock || lastVC[objectName] + 1; options.senderID = [false, null].includes(options.senderID) ? null : @@ -762,7 +765,7 @@ }) } - floCloudAPI.closeRequest = function(requestID) { + floCloudAPI.closeRequest = function (requestID) { return new Promise((resolve, reject) => { let conn = _liveRequest[requestID] if (!conn) @@ -776,7 +779,7 @@ } //reset or initialize an object and send it to cloud - floCloudAPI.resetObjectData = function(objectName, options = {}) { + floCloudAPI.resetObjectData = function (objectName, options = {}) { return new Promise((resolve, reject) => { let message = { reset: appObjects[objectName] @@ -790,7 +793,7 @@ } //update the diff and send it to cloud - floCloudAPI.updateObjectData = function(objectName, options = {}) { + floCloudAPI.updateObjectData = function (objectName, options = {}) { return new Promise((resolve, reject) => { let message = { diff: diff.find(lastCommit.get(objectName), appObjects[ @@ -809,7 +812,7 @@ findDiff(original, updatedObj) returns an object with the added, deleted and updated differences mergeDiff(original, allDiff) returns a new object from original object merged with all differences (allDiff is returned object of findDiff) */ - var diff = (function() { + var diff = (function () { const isDate = d => d instanceof Date; const isEmpty = o => Object.keys(o).length === 0; const isObject = o => o != null && typeof o === 'object'; @@ -983,23 +986,23 @@ }, {}); }; - const mergeRecursive = (obj1, obj2) => { + const mergeRecursive = (obj1, obj2, deleteMode = false) => { for (var p in obj2) { try { if (obj2[p].constructor == Object) - obj1[p] = mergeRecursive(obj1[p], obj2[p]); + obj1[p] = mergeRecursive(obj1[p], obj2[p], deleteMode); // Property in destination object set; update its value. - else if (Ext.isArray(obj2[p])) { + else if (Array.isArray(obj2[p])) { // obj1[p] = []; if (obj2[p].length < 1) obj1[p] = obj2[p]; else - obj1[p] = mergeRecursive(obj1[p], obj2[p]); + obj1[p] = mergeRecursive(obj1[p], obj2[p], deleteMode); } else - obj1[p] = obj2[p]; + obj1[p] = deleteMode && obj2[p] === null ? undefined : obj2[p]; } catch (e) { // Property in destination object not set; create it and set its value. - obj1[p] = obj2[p]; + obj1[p] = deleteMode && obj2[p] === null ? undefined : obj2[p]; } } return obj1; @@ -1008,20 +1011,13 @@ const cleanse = (obj) => { Object.keys(obj).forEach(key => { var value = obj[key]; - if (typeof value === "object" && value !== null) { - // Recurse... - cleanse(value); - // ...and remove if now "empty" (NOTE: insert your definition of "empty" here) - //if (!Object.keys(value).length) - // delete obj[key]; - } else if (value === null) - delete obj[key]; // null, remove it + if (typeof value === "object" && value !== null) + obj[key] = cleanse(value); + else if (typeof value === 'undefined') + delete obj[key]; // undefined, remove it }); - if (obj.constructor.toString().indexOf("Array") != -1) { - obj = obj.filter(function(el) { - return el != null; - }); - } + if (Array.isArray(obj)) + obj = obj.filter(v => typeof v !== 'undefined'); return obj; } @@ -1037,7 +1033,7 @@ if (Object.keys(diff.updated).length !== 0) obj = mergeRecursive(obj, diff.updated) if (Object.keys(diff.deleted).length !== 0) { - obj = mergeRecursive(obj, diff.deleted) + obj = mergeRecursive(obj, diff.deleted, true) obj = cleanse(obj) } if (Object.keys(diff.added).length !== 0) diff --git a/scripts/floCrypto.js b/scripts/floCrypto.js index 6671ffa..563d77f 100644 --- a/scripts/floCrypto.js +++ b/scripts/floCrypto.js @@ -1,4 +1,4 @@ -(function(EXPORTS) { //floCrypto v2.3.3d +(function (EXPORTS) { //floCrypto v2.3.3e /* FLO Crypto Operators */ 'use strict'; const floCrypto = EXPORTS; @@ -78,14 +78,14 @@ } //generate a random Interger within range - floCrypto.randInt = function(min, max) { + floCrypto.randInt = function (min, max) { min = Math.ceil(min); max = Math.floor(max); return Math.floor(securedMathRandom() * (max - min + 1)) + min; } //generate a random String within length (options : alphaNumeric chars only) - floCrypto.randString = function(length, alphaNumeric = true) { + floCrypto.randString = function (length, alphaNumeric = true) { var result = ''; var characters = alphaNumeric ? 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789' : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_+-./*?@#&$<>=[]{}():'; @@ -95,7 +95,7 @@ } //Encrypt Data using public-key - floCrypto.encryptData = function(data, receiverPublicKeyHex) { + floCrypto.encryptData = function (data, receiverPublicKeyHex) { var senderECKeyData = getSenderPublicKeyString(); var senderDerivedKey = deriveSharedKeySender(receiverPublicKeyHex, senderECKeyData.privateKey); let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue; @@ -107,7 +107,7 @@ } //Decrypt Data using private-key - floCrypto.decryptData = function(data, privateKeyHex) { + floCrypto.decryptData = function (data, privateKeyHex) { var receiverECKeyData = {}; if (typeof privateKeyHex !== "string") throw new Error("No private key found."); let privateKey = wifToDecimal(privateKeyHex, true); @@ -120,7 +120,7 @@ } //Sign data using private-key - floCrypto.signData = function(data, privateKeyHex) { + floCrypto.signData = function (data, privateKeyHex) { var key = new Bitcoin.ECKey(privateKeyHex); var messageHash = Crypto.SHA256(data); var messageSign = Bitcoin.ECDSA.sign(messageHash, key.priv); @@ -129,7 +129,7 @@ } //Verify signatue of the data using public-key - floCrypto.verifySign = function(data, signatureHex, publicKeyHex) { + floCrypto.verifySign = function (data, signatureHex, publicKeyHex) { var msgHash = Crypto.SHA256(data); var sigBytes = Crypto.util.hexToBytes(signatureHex); var publicKeyPoint = ecparams.getCurve().decodePointHex(publicKeyHex); @@ -138,7 +138,7 @@ } //Generates a new flo ID and returns private-key, public-key and floID - const generateNewID = floCrypto.generateNewID = function() { + const generateNewID = floCrypto.generateNewID = function () { var key = new Bitcoin.ECKey(false); key.setCompressed(true); return { @@ -168,7 +168,7 @@ }); //Returns public-key from private-key - floCrypto.getPubKeyHex = function(privateKeyHex) { + floCrypto.getPubKeyHex = function (privateKeyHex) { if (!privateKeyHex) return null; var key = new Bitcoin.ECKey(privateKeyHex); @@ -179,7 +179,7 @@ } //Returns flo-ID from public-key or private-key - floCrypto.getFloID = function(keyHex) { + floCrypto.getFloID = function (keyHex) { if (!keyHex) return null; try { @@ -192,7 +192,7 @@ } } - floCrypto.getAddress = function(privateKeyHex, strict = false) { + floCrypto.getAddress = function (privateKeyHex, strict = false) { if (!privateKeyHex) return; var key = new Bitcoin.ECKey(privateKeyHex); @@ -212,7 +212,7 @@ } //Verify the private-key for the given public-key or flo-ID - floCrypto.verifyPrivKey = function(privateKeyHex, pubKey_floID, isfloID = true) { + floCrypto.verifyPrivKey = function (privateKeyHex, pubKey_floID, isfloID = true) { if (!privateKeyHex || !pubKey_floID) return false; try { @@ -232,7 +232,7 @@ } //Check if the given flo-id is valid or not - floCrypto.validateFloID = function(floID) { + floCrypto.validateFloID = function (floID) { if (!floID) return false; try { @@ -244,7 +244,7 @@ } //Check if the given address (any blockchain) is valid or not - floCrypto.validateAddr = function(address, std = true, bech = true) { + floCrypto.validateAddr = function (address, std = true, bech = true) { let raw = decodeAddress(address); if (!raw) return false; @@ -267,7 +267,7 @@ } //Check the public-key for the address (any blockchain) - floCrypto.verifyPubKey = function(pubKeyHex, address) { + floCrypto.verifyPubKey = function (pubKeyHex, address) { let raw = decodeAddress(address), pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true @@ -276,12 +276,18 @@ } //Convert the given address (any blockchain) to equivalent floID - floCrypto.toFloID = function(address) { + floCrypto.toFloID = function (address, options = null) { if (!address) return; let raw = decodeAddress(address); if (!raw) return; + else if (options) { + if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version))) + return; + if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version))) + return; + } raw.bytes.unshift(bitjs.pub); let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, { asBytes: true @@ -292,7 +298,7 @@ } //Checks if the given addresses (any blockchain) are same (w.r.t keys) - floCrypto.isSameAddr = function(addr1, addr2) { + floCrypto.isSameAddr = function (addr1, addr2) { if (!addr1 || !addr2) return; let raw1 = decodeAddress(addr1), @@ -303,7 +309,7 @@ return raw1.hex === raw2.hex; } - const decodeAddress = floCrypto.decodeAddr = function(address) { + const decodeAddress = floCrypto.decodeAddr = function (address) { if (!address) return; else if (address.length == 33 || address.length == 34) { //legacy encoding @@ -338,7 +344,7 @@ } //Split the str using shamir's Secret and Returns the shares - floCrypto.createShamirsSecretShares = function(str, total_shares, threshold_limit) { + floCrypto.createShamirsSecretShares = function (str, total_shares, threshold_limit) { try { if (str.length > 0) { var strHex = shamirSecretShare.str2hex(str); @@ -352,7 +358,7 @@ } //Returns the retrived secret by combining the shamirs shares - const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function(sharesArray) { + const retrieveShamirSecret = floCrypto.retrieveShamirSecret = function (sharesArray) { try { if (sharesArray.length > 0) { var comb = shamirSecretShare.combine(sharesArray.slice(0, sharesArray.length)); @@ -366,7 +372,7 @@ } //Verifies the shares and str - floCrypto.verifyShamirsSecret = function(sharesArray, str) { + floCrypto.verifyShamirsSecret = function (sharesArray, str) { if (!str) return null; else if (retrieveShamirSecret(sharesArray) === str) @@ -375,7 +381,7 @@ return false; } - const validateASCII = floCrypto.validateASCII = function(string, bool = true) { + const validateASCII = floCrypto.validateASCII = function (string, bool = true) { if (typeof string !== "string") return null; if (bool) { @@ -393,8 +399,8 @@ if (x < 32 || x > 127) if (x in invalids) invalids[string[i]].push(i) - else - invalids[string[i]] = [i]; + else + invalids[string[i]] = [i]; } if (Object.keys(invalids).length) return invalids; @@ -403,7 +409,7 @@ } } - floCrypto.convertToASCII = function(string, mode = 'soft-remove') { + floCrypto.convertToASCII = function (string, mode = 'soft-remove') { let chars = validateASCII(string, false); if (chars === true) return string; @@ -414,9 +420,9 @@ ascii_alternatives.split('\n').forEach(a => refAlt[a[0]] = a.slice(2)); mode = mode.toLowerCase(); if (mode === "hard-unicode") - convertor = (c) => `\\u${('000'+c.charCodeAt().toString(16)).slice(-4)}`; + convertor = (c) => `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`; else if (mode === "soft-unicode") - convertor = (c) => refAlt[c] || `\\u${('000'+c.charCodeAt().toString(16)).slice(-4)}`; + convertor = (c) => refAlt[c] || `\\u${('000' + c.charCodeAt().toString(16)).slice(-4)}`; else if (mode === "hard-remove") convertor = c => ""; else if (mode === "soft-remove") @@ -428,7 +434,7 @@ return result; } - floCrypto.revertUnicode = function(string) { + floCrypto.revertUnicode = function (string) { return string.replace(/\\u[\dA-F]{4}/gi, m => String.fromCharCode(parseInt(m.replace(/\\u/g, ''), 16))); } diff --git a/scripts/messenger.js b/scripts/messenger.js index 777b714..d52138f 100644 --- a/scripts/messenger.js +++ b/scripts/messenger.js @@ -1,4 +1,4 @@ -(function() { +(function () { const messenger = window.messenger = {}; const user = { @@ -136,7 +136,7 @@ }) } - const initUserDB = function() { + const initUserDB = function () { return new Promise((resolve, reject) => { var obj = { messages: {}, @@ -164,7 +164,7 @@ }) } - messenger.blockUser = function(floID) { + messenger.blockUser = function (floID) { return new Promise((resolve, reject) => { if (_loaded.blocked.has(floID)) return resolve("User is already blocked"); @@ -175,7 +175,7 @@ }) } - messenger.unblockUser = function(floID) { + messenger.unblockUser = function (floID) { return new Promise((resolve, reject) => { if (!_loaded.blocked.has(floID)) return resolve("User is not blocked"); @@ -186,7 +186,7 @@ }) } - messenger.sendMessage = function(message, receiver) { + messenger.sendMessage = function (message, receiver) { return new Promise((resolve, reject) => { sendRaw(message, receiver, "MESSAGE").then(result => { let vc = result.vectorClock; @@ -207,7 +207,7 @@ }) } - messenger.sendMail = function(subject, content, recipients, prev = null) { + messenger.sendMail = function (subject, content, recipients, prev = null) { return new Promise((resolve, reject) => { if (!Array.isArray(recipients)) recipients = [recipients] @@ -312,7 +312,7 @@ messenger.respond_pubKey = (req_id, message = '') => sendResponse(req_id, message, false); const processData = {}; - processData.direct = function() { + processData.direct = function () { return (unparsed, newInbox) => { //store the pubKey if not stored already floDapps.storePubKey(unparsed.senderID, unparsed.pubKey); @@ -433,7 +433,7 @@ directConnID = undefined; } const parseData = processData.direct(); - let callbackFn = function(dataSet, error) { + let callbackFn = function (dataSet, error) { if (error) return console.error(error) let newInbox = { @@ -461,14 +461,14 @@ UI.direct(newInbox) } floCloudAPI.requestApplicationData(null, { - receiverID: user.id, - lowerVectorClock: _loaded.appendix.lastReceived + 1, - callback: callbackFn - }).then(conn_id => directConnID = conn_id) + receiverID: user.id, + lowerVectorClock: _loaded.appendix.lastReceived + 1, + callback: callbackFn + }).then(conn_id => directConnID = conn_id) .catch(error => console.error("request-direct:", error)); } - messenger.getMail = function(mailRef) { + messenger.getMail = function (mailRef) { return new Promise((resolve, reject) => { compactIDB.readData("mails", mailRef).then(mail => { mail.content = decrypt(mail.content) @@ -477,7 +477,7 @@ }); } - const getChatOrder = messenger.getChatOrder = function(separate = false) { + const getChatOrder = messenger.getChatOrder = function (separate = false) { let result; if (separate) { result = {}; @@ -496,11 +496,11 @@ return result; } - messenger.storeContact = function(floID, name) { + messenger.storeContact = function (floID, name) { return floDapps.storeContact(floID, name) } - const loadDataFromIDB = function(defaultList = true) { + const loadDataFromIDB = function (defaultList = true) { return new Promise((resolve, reject) => { if (defaultList) dataList = ["mails", "marked", "groups", "pipeline", "chats", "blocked", "appendix"] @@ -553,19 +553,19 @@ }) } - messenger.addMark = function(key, mark) { + messenger.addMark = function (key, mark) { if (_loaded.marked.hasOwnProperty(key) && !_loaded.marked[key].includes(mark)) _loaded.marked[key].push(mark) return addMark(key, mark) } - messenger.removeMark = function(key, mark) { + messenger.removeMark = function (key, mark) { if (_loaded.marked.hasOwnProperty(key)) _loaded.marked[key] = _loaded.marked[key].filter(v => v !== mark) return removeMark(key, mark) } - messenger.addChat = function(chatID) { + messenger.addChat = function (chatID) { return new Promise((resolve, reject) => { compactIDB.addData("chats", 0, chatID) .then(result => resolve("Added chat")) @@ -573,7 +573,7 @@ }) } - messenger.rmChat = function(chatID) { + messenger.rmChat = function (chatID) { return new Promise((resolve, reject) => { compactIDB.removeData("chats", chatID) .then(result => resolve("Chat removed")) @@ -581,7 +581,7 @@ }) } - messenger.clearChat = function(chatID) { + messenger.clearChat = function (chatID) { return new Promise((resolve, reject) => { let options = { lowerKey: `${chatID}|`, @@ -598,7 +598,7 @@ }) } - const getChat = messenger.getChat = function(chatID) { + const getChat = messenger.getChat = function (chatID) { return new Promise((resolve, reject) => { let options = { lowerKey: `${chatID}|`, @@ -613,7 +613,7 @@ }) } - messenger.backupData = function() { + messenger.backupData = function () { return new Promise((resolve, reject) => { loadDataFromIDB(false).then(data => { delete data.appendix.AESKey; @@ -633,7 +633,7 @@ }) } - const parseBackup = messenger.parseBackup = function(blob) { + const parseBackup = messenger.parseBackup = function (blob) { return new Promise((resolve, reject) => { if (blob instanceof Blob || blob instanceof File) { let reader = new FileReader(); @@ -663,7 +663,7 @@ }) } - messenger.restoreData = function(arg) { + messenger.restoreData = function (arg) { return new Promise((resolve, reject) => { if (arg instanceof Blob || arg instanceof File) var parseData = parseBackup @@ -718,7 +718,7 @@ }) } - messenger.clearUserData = function() { + messenger.clearUserData = function () { return new Promise((resolve, reject) => { let promises = [ compactIDB.deleteDB(), @@ -732,7 +732,7 @@ //group feature - messenger.createGroup = function(groupname, description = '') { + messenger.createGroup = function (groupname, description = '') { return new Promise((resolve, reject) => { if (!groupname) return reject("Invalid Group Name") let id = floCrypto.generateNewID(); @@ -760,7 +760,7 @@ }) } - messenger.changeGroupName = function(groupID, name) { + messenger.changeGroupName = function (groupID, name) { return new Promise((resolve, reject) => { let groupInfo = _loaded.groups[groupID] if (user.id !== groupInfo.admin) @@ -772,7 +772,7 @@ }) } - messenger.changeGroupDescription = function(groupID, description) { + messenger.changeGroupDescription = function (groupID, description) { return new Promise((resolve, reject) => { let groupInfo = _loaded.groups[groupID] if (user.id !== groupInfo.admin) @@ -784,7 +784,7 @@ }) } - messenger.addGroupMembers = function(groupID, newMem, note = undefined) { + messenger.addGroupMembers = function (groupID, newMem, note = undefined) { return new Promise((resolve, reject) => { if (!Array.isArray(newMem) && typeof newMem === "string") newMem = [newMem] @@ -793,7 +793,7 @@ imem2 = [] newMem.forEach(m => !floCrypto.validateAddr(m) ? imem1.push(m) : - m in floGlobals.pubKeys ? null : imem2.push(m) + m in floGlobals.pubKeys ? null : imem2.push(m) ); if (imem1.length) return reject(`Invalid Members(floIDs): ${imem1}`) @@ -813,8 +813,8 @@ for (let i in results) if (results[i].status === "fulfilled") success.push(newMem[i]) - else if (results[i].status === "rejected") - failed.push(newMem[i]) + else if (results[i].status === "rejected") + failed.push(newMem[i]) let message = encrypt(success.join("|"), k) sendRaw(message, groupID, "ADD_MEMBERS", false, note) .then(r => resolve(`Members added: ${success}`)) @@ -823,7 +823,7 @@ }) } - messenger.rmGroupMembers = function(groupID, rmMem, note = undefined) { + messenger.rmGroupMembers = function (groupID, rmMem, note = undefined) { return new Promise((resolve, reject) => { if (!Array.isArray(rmMem) && typeof rmMem === "string") rmMem = [rmMem] @@ -843,7 +843,7 @@ }) } - const revokeKey = messenger.revokeKey = function(groupID) { + const revokeKey = messenger.revokeKey = function (groupID) { return new Promise((resolve, reject) => { let groupInfo = _loaded.groups[groupID] if (user.id !== groupInfo.admin) @@ -858,7 +858,7 @@ }) } - messenger.sendGroupMessage = function(message, groupID) { + messenger.sendGroupMessage = function (message, groupID) { return new Promise((resolve, reject) => { let k = _loaded.groups[groupID].eKey message = encrypt(message, k) @@ -868,7 +868,7 @@ }) } - const disableGroup = messenger.disableGroup = function(groupID) { + const disableGroup = messenger.disableGroup = function (groupID) { return new Promise((resolve, reject) => { if (!_loaded.groups[groupID]) return reject("Group not found"); @@ -885,7 +885,7 @@ }) } - processData.group = function(groupID) { + processData.group = function (groupID) { return (unparsed, newInbox) => { if (!_loaded.groups[groupID].members.includes(unparsed.senderID)) return; @@ -958,7 +958,7 @@ } const parseData = processData.group(groupID); - let callbackFn = function(dataSet, error) { + let callbackFn = function (dataSet, error) { if (error) return console.error(error) console.info(dataSet) @@ -988,16 +988,16 @@ UI.group(newInbox); } floCloudAPI.requestApplicationData(null, { - receiverID: groupID, - lowerVectorClock: _loaded.appendix[`lastReceived_${groupID}`] + 1, - callback: callbackFn - }).then(conn_id => groupConnID[groupID] = conn_id) + receiverID: groupID, + lowerVectorClock: _loaded.appendix[`lastReceived_${groupID}`] + 1, + callback: callbackFn + }).then(conn_id => groupConnID[groupID] = conn_id) .catch(error => console.error(`request-group(${groupID}):`, error)) } //messenger startups - messenger.init = function() { + messenger.init = function () { return new Promise((resolve, reject) => { initUserDB().then(result => { console.debug(result); @@ -1030,7 +1030,7 @@ }) } - const loadDataFromBlockchain = messenger.loadDataFromBlockchain = function() { + const loadDataFromBlockchain = messenger.loadDataFromBlockchain = function () { return new Promise((resolve, reject) => { let user_floID = floCrypto.toFloID(user.id); if (!user_floID) @@ -1064,7 +1064,7 @@ //BTC multisig application const MultiSig = messenger.multisig = {} const TYPE_BTC_MULTISIG = "btc_multisig"; - MultiSig.createAddress = function(pubKeys, minRequired) { + MultiSig.createAddress = function (pubKeys, minRequired) { return new Promise(async (resolve, reject) => { let co_owners = pubKeys.map(p => floCrypto.getFloID(p)); if (co_owners.includes(null)) @@ -1095,7 +1095,7 @@ }) } - MultiSig.listAddress = function() { + MultiSig.listAddress = function () { return new Promise((resolve, reject) => { let options = { lowerKey: `${TYPE_BTC_MULTISIG}|`, @@ -1126,7 +1126,7 @@ }) } - MultiSig.createTx = function(address, redeemScript, receivers, amounts, fee = null) { + MultiSig.createTx = function (address, redeemScript, receivers, amounts, fee = null, options = {}) { return new Promise(async (resolve, reject) => { let decode = coinjs.script().decodeRedeemScript(redeemScript); if (!decode || decode.address !== address || decode.type !== "multisig__") @@ -1137,10 +1137,10 @@ return reject("Invalid multisig (required is greater than users)"); let co_owners = decode.pubkeys.map(p => floCrypto.getFloID(p)); let privateKey = await floDapps.user.private; - btcOperator.createMultiSigTx(address, redeemScript, receivers, amounts, fee).then(tx => { - tx = btcOperator.signTx(tx, privateKey); + btcOperator.createMultiSigTx(address, redeemScript, receivers, amounts, fee, options).then(({ tx_hex }) => { + tx_hex = btcOperator.signTx(tx_hex, privateKey); createPipeline(TYPE_BTC_MULTISIG, co_owners, 32).then(pipeline => { - let message = encrypt(tx, pipeline.eKey); + let message = encrypt(tx_hex, pipeline.eKey); sendRaw(message, pipeline.id, "TRANSACTION", false) .then(result => resolve(pipeline.id)) .catch(error => reject(error)) @@ -1149,7 +1149,7 @@ }) } - MultiSig.signTx = function(pipeID) { + MultiSig.signTx = function (pipeID) { return new Promise((resolve, reject) => { if (_loaded.pipeline[pipeID].disabled) return reject("Pipeline is already closed"); @@ -1165,8 +1165,7 @@ tx_hex: tx_hex_signed }); debugger; - btcOperator.broadcast(tx_hex_signed).then(result => { - let txid = result.txid; + btcOperator.broadcastTx(tx_hex_signed).then(txid => { console.debug(txid); sendRaw(encrypt(txid, pipeline.eKey), pipeline.id, "BROADCAST", false) .then(result => resolve({ @@ -1180,15 +1179,15 @@ } //Pipelines - const createPipeline = function(model, members, ekeySize = 16) { + const createPipeline = function (model, members, ekeySize = 16) { return new Promise((resolve, reject) => { //validate members let imem1 = [], imem2 = [] members.forEach(m => !floCrypto.validateAddr(m) ? imem1.push(m) : - m in floGlobals.pubKeys ? null : - m != user.id ? imem2.push(m) : null + m in floGlobals.pubKeys ? null : + m != user.id ? imem2.push(m) : null ); if (imem1.length) return reject(`Invalid Members(floIDs): ${imem1}`); @@ -1226,7 +1225,7 @@ } let parseData = processData.pipeline[model](pipeID); - let callbackFn = function(dataSet, error) { + let callbackFn = function (dataSet, error) { if (error) return console.error(error); console.info(dataSet) @@ -1253,14 +1252,14 @@ } floCloudAPI.requestApplicationData(null, { - receiverID: pipeID, - lowerVectorClock: _loaded.appendix[`lastReceived_${pipeID}`] + 1, - callback: callbackFn - }).then(conn_id => pipeConnID[pipeID] = conn_id) + receiverID: pipeID, + lowerVectorClock: _loaded.appendix[`lastReceived_${pipeID}`] + 1, + callback: callbackFn + }).then(conn_id => pipeConnID[pipeID] = conn_id) .catch(error => console.error(`request-pipeline(${pipeID}):`, error)) } - const disablePipeline = messenger.disablePipeline = function(pipeID) { + const disablePipeline = messenger.disablePipeline = function (pipeID) { console.debug(JSON.stringify(pipeConnID), pipeConnID[pipeID]) return new Promise((resolve, reject) => { if (!_loaded.pipeline[pipeID]) @@ -1278,7 +1277,7 @@ }) } - messenger.sendPipelineMessage = function(message, pipeID) { + messenger.sendPipelineMessage = function (message, pipeID) { return new Promise((resolve, reject) => { let k = _loaded.pipeline[pipeID].eKey; if (k) message = encrypt(message, k); @@ -1289,7 +1288,7 @@ } processData.pipeline = {}; - processData.pipeline[TYPE_BTC_MULTISIG] = function(pipeID) { + processData.pipeline[TYPE_BTC_MULTISIG] = function (pipeID) { return (unparsed, newInbox) => { if (!_loaded.pipeline[pipeID].members.includes(floCrypto.toFloID(unparsed.senderID))) return;