Update stdops

- Added option parameter to multisig.createTx in messenger module. option values {} for btcOperator.createMultiSigTx {fee_from_receiver, change_address}
This commit is contained in:
sairajzero 2022-11-18 18:23:51 +05:30
parent 59b0744b3f
commit 1e9c414bad
5 changed files with 419 additions and 235 deletions

View File

@ -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>.*<\/result>/);
if (!r)
reject(resultText);
else {
r = r.pop().replace('<result>', '').replace('</result>', '');
if (r == '1') {
let txid = resultText.match(/<txid>.*<\/txid>/).pop().replace('<txid>', '').replace('</txid>', '');
resolve(txid);
} else if (r == '0') {
let error = resultText.match(/<response>.*<\/response>/).pop().replace('<response>', '').replace('</response>', '');
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))

View File

@ -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

View File

@ -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)

View File

@ -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)));
}

View File

@ -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;