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 */ /* BTC Crypto and API Operator */
const btcOperator = EXPORTS; const btcOperator = EXPORTS;
@ -16,14 +16,14 @@
}) })
}; };
const SIGN_SIZE = 73; const SATOSHI_IN_BTC = 1e8;
function get_fee_rate() { function get_fee_rate() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
fetch('https://api.blockchain.info/mempool/fees').then(response => { fetch('https://api.blockchain.info/mempool/fees').then(response => {
if (response.ok) if (response.ok)
response.json() response.json()
.then(result => resolve(result.regular)) .then(result => resolve(parseFloat((result.regular / SATOSHI_IN_BTC).toFixed(8))))
.catch(error => reject(error)); .catch(error => reject(error));
else else
reject(response); reject(response);
@ -31,17 +31,31 @@
}) })
} }
const broadcast = btcOperator.broadcast = rawtx => new Promise((resolve, reject) => { const broadcastTx = btcOperator.broadcastTx = rawTxHex => new Promise((resolve, reject) => {
$.ajax({ let url = 'https://coinb.in/api/?uid=1&key=12345678901234567890123456789012&setmodule=bitcoin&request=sendrawtransaction';
type: "POST", fetch(url, {
url: URL + "send_tx/BTC/", method: 'POST',
data: { headers: {
"tx_hex": rawtx 'Content-Type': 'application/x-www-form-urlencoded'
}, },
dataType: "json", body: "rawtx=" + rawTxHex
error: e => reject(e.responseJSON), }).then(response => {
success: r => r.status === "success" ? resolve(r.data) : reject(r) 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, { Object.defineProperties(btcOperator, {
@ -204,6 +218,17 @@
.catch(error => reject(error)) .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) { function _redeemScript(addr, key) {
let decode = coinjs.addressDecode(addr); let decode = coinjs.addressDecode(addr);
switch (decode.type) { 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) { function validateTxParameters(parameters) {
let invalids = []; let invalids = [];
//sender-ids //sender-ids
@ -249,8 +307,8 @@
parameters.receivers.forEach(id => !validateAddress(id) ? invalids.push(id) : null); parameters.receivers.forEach(id => !validateAddress(id) ? invalids.push(id) : null);
if (invalids.length) if (invalids.length)
throw "Invalid receivers:" + invalids; throw "Invalid receivers:" + invalids;
if (parameters.change_addr && !validateAddress(parameters.change_addr)) if (parameters.change_address && !validateAddress(parameters.change_address))
throw "Invalid change_address:" + parameters.change_addr; throw "Invalid change_address:" + parameters.change_address;
//fee and amounts //fee and amounts
if ((typeof parameters.fee !== "number" || parameters.fee <= 0) && parameters.fee !== null) //fee = null (auto calc) if ((typeof parameters.fee !== "number" || parameters.fee <= 0) && parameters.fee !== null) //fee = null (auto calc)
throw "Invalid fee:" + parameters.fee; throw "Invalid fee:" + parameters.fee;
@ -265,48 +323,88 @@
return parameters; return parameters;
} }
const TMP_FEE = 0.00001; function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_address, fee_from_receiver) {
function createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let auto_fee = false, let total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
if (fee === null) {
auto_fee = true;
fee = TMP_FEE;
}
const tx = coinjs.transaction(); const tx = coinjs.transaction();
addUTXOs(tx, senders, redeemScripts, total_amount + fee).then(result => { let output_size = addOutputs(tx, receivers, amounts, change_address);
if (result > 0) addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver).then(result => {
return reject("Insufficient Balance"); if (result.change_amount > 0) //add change amount if any
let change = addOutputs(tx, receivers, amounts, Math.abs(result), change_addr); tx.outs[tx.outs.length - 1].value = parseInt(result.change_amount * SATOSHI_IN_BTC); //values are in satoshi
if (!auto_fee) if (fee_from_receiver) { //deduce fee from receivers if fee_from_receiver
return resolve(tx); let fee_remaining = parseInt(result.fee * SATOSHI_IN_BTC);
autoFeeCalc(tx).then(fee_calc => { for (let i = 0; i < tx.outs.length - 1 && fee_remaining > 0; i++) {
fee = Math.round((fee * 1) * 1e8); //satoshi convertion if (fee_remaining < tx.outs[i].value) {
if (!change) tx.outs[i].value -= fee_remaining;
tx.addoutput(change_addr, 0); fee_remaining = 0;
editFee(tx, fee, fee_calc); } else {
resolve(tx); 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)) }).catch(error => reject(error))
}) })
}
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, n = 0) { function addUTXOs(tx, senders, redeemScripts, required_amount, fee_rate, rec_args = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
required_amount = parseFloat(required_amount.toFixed(8)); required_amount = parseFloat(required_amount.toFixed(8));
if (required_amount <= 0 || n >= senders.length) if (typeof rec_args.n === "undefined") {
return resolve(required_amount); rec_args.n = 0;
let addr = senders[n], rec_args.input_size = 0;
rs = redeemScripts[n]; 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 => { fetch_api(`get_tx_unspent/BTC/${addr}`).then(result => {
let utxos = result.data.txs; let utxos = result.data.txs;
console.debug("add-utxo", addr, rs, required_amount, utxos); console.debug("add-utxo", addr, rs, required_amount, utxos);
for (let i = 0; i < utxos.length && required_amount > 0; i++) { for (let i = 0; i < utxos.length && required_amount > 0; i++) {
if (!utxos[i].confirmations) //ignore unconfirmed utxo if (!utxos[i].confirmations) //ignore unconfirmed utxo
continue; continue;
required_amount -= parseFloat(utxos[i].value);
var script; var script;
if (!rs || !rs.length) //legacy script if (!rs || !rs.length) //legacy script
script = utxos[i].script_hex; script = utxos[i].script_hex;
@ -315,29 +413,38 @@
let s = coinjs.script(); let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(rs)); s.writeBytes(Crypto.util.hexToBytes(rs));
s.writeOp(0); 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); script = Crypto.util.bytesToHex(s.buffer);
} else //redeemScript for multisig } else //redeemScript for multisig
script = rs; script = rs;
tx.addinput(utxos[i].txid, utxos[i].output_no, script, 0xfffffffd /*sequence*/); //0xfffffffd for Replace-by-fee 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)) .then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}) })
} }
function addOutputs(tx, receivers, amounts, change, change_addr) { function addOutputs(tx, receivers, amounts, change_address) {
for (let i in receivers) let size = 0;
for (let i in receivers) {
tx.addoutput(receivers[i], amounts[i]); tx.addoutput(receivers[i], amounts[i]);
if (parseFloat(change.toFixed(8)) > 0) { size += _sizePerOutput(receivers[i]);
tx.addoutput(change_addr, change); }
return true; tx.addoutput(change_address, 0);
} else size += _sizePerOutput(change_address);
return false; return size;
} }
/*
function autoFeeCalc(tx) { function autoFeeCalc(tx) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
get_fee_rate().then(fee_rate => { get_fee_rate().then(fee_rate => {
@ -362,18 +469,30 @@
function editFee(tx, current_fee, target_fee, index = -1) { function editFee(tx, current_fee, target_fee, index = -1) {
//values are in satoshi //values are in satoshi
index = parseInt(index >= 0 ? index : tx.out.length - index); index = parseInt(index >= 0 ? index : tx.outs.length - index);
if (index < 0 || index >= tx.out.length) if (index < 0 || index >= tx.outs.length)
throw "Invalid index"; throw "Invalid index";
let edit_value = parseInt(current_fee - target_fee), //rip of any decimal places 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) if (edit_value < 0 && edit_value > current_value)
throw "Insufficient value at vout"; 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); 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) => { return new Promise((resolve, reject) => {
try { try {
({ ({
@ -387,7 +506,7 @@
receivers, receivers,
amounts, amounts,
fee, fee,
change_addr change_address: options.change_address
})); }));
} catch (e) { } catch (e) {
return reject(e) return reject(e)
@ -402,19 +521,17 @@
if (redeemScripts.includes(null)) //TODO: segwit if (redeemScripts.includes(null)) //TODO: segwit
return reject("Unable to get redeem-script"); return reject("Unable to get redeem-script");
//create transaction //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()); 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 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()); console.debug("Signed:", tx.serialize());
debugger; resolve(result);
broadcast(tx.serialize())
.then(result => resolve(result))
.catch(error => reject(error));
}).catch(error => reject(error)); }).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) => { return new Promise((resolve, reject) => {
try { try {
({ ({
@ -426,7 +543,7 @@
receivers, receivers,
amounts, amounts,
fee, fee,
change_addr change_address: options.change_address
})); }));
} catch (e) { } catch (e) {
return reject(e) return reject(e)
@ -435,13 +552,15 @@
if (redeemScripts.includes(null)) //TODO: segwit if (redeemScripts.includes(null)) //TODO: segwit
return reject("Unable to get redeem-script"); return reject("Unable to get redeem-script");
//create transaction //create transaction
createTransaction(senders, redeemScripts, receivers, amounts, fee, change_addr || senders[0]) createTransaction(senders, redeemScripts, receivers, amounts, fee, options.change_address || senders[0], options.fee_from_receiver).then(result => {
.then(tx => resolve(tx.serialize())) result.tx_hex = result.transaction.serialize();
.catch(error => reject(error)) 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) => { return new Promise((resolve, reject) => {
//validate tx parameters //validate tx parameters
if (validateAddress(sender) !== "multisig") if (validateAddress(sender) !== "multisig")
@ -459,15 +578,19 @@
} = validateTxParameters({ } = validateTxParameters({
receivers, receivers,
amounts, amounts,
fee fee,
change_address: options.change_address
})); }));
} catch (e) { } catch (e) {
return reject(e) return reject(e)
} }
//create transaction //create transaction
createTransaction([sender], [redeemScript], receivers, amounts, fee, sender) createTransaction([sender], [redeemScript], receivers, amounts, fee, options.change_address || sender, options.fee_from_receiver).then(result => {
.then(tx => resolve(tx.serialize())) result.tx_hex = result.transaction.serialize();
.catch(error => reject(error)) delete result.transaction;
resolve(result);
}).catch(error => reject(error))
}) })
} }
@ -494,7 +617,7 @@
return tx.serialize(); return tx.serialize();
} }
btcOperator.checkSigned = function (tx, bool = true) { const checkSigned = btcOperator.checkSigned = function (tx, bool = true) {
tx = deserializeTx(tx); tx = deserializeTx(tx);
let n = []; let n = [];
for (let i in tx.ins) { for (let i in tx.ins) {
@ -533,6 +656,63 @@
return true; 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) => { btcOperator.getTx = txid => new Promise((resolve, reject) => {
fetch_api(`get_tx/BTC/${txid}`) fetch_api(`get_tx/BTC/${txid}`)
.then(result => resolve(result.data)) .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*/ /* FLO Blockchain Operator to send/receive data from blockchain using API calls*/
'use strict'; 'use strict';
const floBlockchainAPI = EXPORTS; const floBlockchainAPI = EXPORTS;
@ -11,6 +11,7 @@
}, },
sendAmt: 0.001, sendAmt: 0.001,
fee: 0.0005, fee: 0.0005,
minChangeAmt: 0.0005,
receiverID: floGlobals.adminID receiverID: floGlobals.adminID
}; };
@ -171,7 +172,7 @@
else { else {
trx.addoutput(receiverAddr, sendAmt); trx.addoutput(receiverAddr, sendAmt);
var change = utxoAmt - sendAmt - fee; var change = utxoAmt - sendAmt - fee;
if (change > 0) if (change > DEFAULT.minChangeAmt)
trx.addoutput(senderAddr, change); trx.addoutput(senderAddr, change);
trx.addflodata(floData.replace(/\n/g, ' ')); trx.addflodata(floData.replace(/\n/g, ' '));
var signedTxHash = trx.sign(privKey, 1); var signedTxHash = trx.sign(privKey, 1);
@ -484,8 +485,8 @@
floBlockchainAPI.readData = function (addr, options = {}) { floBlockchainAPI.readData = function (addr, options = {}) {
options.limit = options.limit || 0; options.limit = options.limit || 0;
options.ignoreOld = options.ignoreOld || 0; options.ignoreOld = options.ignoreOld || 0;
if (typeof options.sender === "string") options.sender = [options.sender]; if (typeof options.senders === "string") options.senders = [options.senders];
if (typeof options.receiver === "string") options.receiver = [options.receiver]; if (typeof options.receivers === "string") options.receivers = [options.receivers];
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => {
var newItems = response.totalItems - options.ignoreOld; var newItems = response.totalItems - options.ignoreOld;
@ -520,10 +521,10 @@
} }
if (!flag) continue; if (!flag) continue;
} }
if (Array.isArray(options.sender)) { if (Array.isArray(options.senders)) {
let flag = false; let flag = false;
for (let vin of response.items[i].vin) for (let vin of response.items[i].vin)
if (options.sender.includes(vin.addr)) { if (options.senders.includes(vin.addr)) {
flag = true; flag = true;
break; break;
} }
@ -538,10 +539,10 @@
} }
if (!flag) continue; if (!flag) continue;
} }
if (Array.isArray(options.receiver)) { if (Array.isArray(options.receivers)) {
let flag = false; let flag = false;
for (let vout of response.items[i].vout) 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; flag = true;
break; break;
} }
@ -555,6 +556,8 @@
d.txid = response.items[i].txid; d.txid = response.items[i].txid;
d.time = response.items[i].time; d.time = response.items[i].time;
d.blockheight = response.items[i].blockheight; 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; d.data = response.items[i].floData;
filteredData.push(d); filteredData.push(d);
} else } 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*/ /* FLO Cloud operations to send/request application data*/
'use strict'; 'use strict';
const floCloudAPI = EXPORTS; const floCloudAPI = EXPORTS;
@ -79,6 +79,9 @@
get: () => generalData, get: () => generalData,
set: data => generalData = data set: data => generalData = data
}, },
generalDataset: {
value: (type, options = {}) => generalData[filterKey(type, options)]
},
lastVC: { lastVC: {
get: () => lastVC, get: () => lastVC,
set: vc => lastVC = vc set: vc => lastVC = vc
@ -375,7 +378,7 @@
return JSON.parse(decodeURIComponent(escape(atob(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 : '') + return type + (options.comment ? ':' + options.comment : '') +
'|' + (options.group || options.receiverID || DEFAULT.adminID) + '|' + (options.group || options.receiverID || DEFAULT.adminID) +
'|' + (options.application || DEFAULT.application); '|' + (options.application || DEFAULT.application);
@ -983,23 +986,23 @@
}, {}); }, {});
}; };
const mergeRecursive = (obj1, obj2) => { const mergeRecursive = (obj1, obj2, deleteMode = false) => {
for (var p in obj2) { for (var p in obj2) {
try { try {
if (obj2[p].constructor == Object) 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. // Property in destination object set; update its value.
else if (Ext.isArray(obj2[p])) { else if (Array.isArray(obj2[p])) {
// obj1[p] = []; // obj1[p] = [];
if (obj2[p].length < 1) if (obj2[p].length < 1)
obj1[p] = obj2[p]; obj1[p] = obj2[p];
else else
obj1[p] = mergeRecursive(obj1[p], obj2[p]); obj1[p] = mergeRecursive(obj1[p], obj2[p], deleteMode);
} else } else
obj1[p] = obj2[p]; obj1[p] = deleteMode && obj2[p] === null ? undefined : obj2[p];
} catch (e) { } catch (e) {
// Property in destination object not set; create it and set its value. // 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; return obj1;
@ -1008,20 +1011,13 @@
const cleanse = (obj) => { const cleanse = (obj) => {
Object.keys(obj).forEach(key => { Object.keys(obj).forEach(key => {
var value = obj[key]; var value = obj[key];
if (typeof value === "object" && value !== null) { if (typeof value === "object" && value !== null)
// Recurse... obj[key] = cleanse(value);
cleanse(value); else if (typeof value === 'undefined')
// ...and remove if now "empty" (NOTE: insert your definition of "empty" here) delete obj[key]; // undefined, remove it
//if (!Object.keys(value).length)
// delete obj[key];
} else if (value === null)
delete obj[key]; // null, remove it
}); });
if (obj.constructor.toString().indexOf("Array") != -1) { if (Array.isArray(obj))
obj = obj.filter(function(el) { obj = obj.filter(v => typeof v !== 'undefined');
return el != null;
});
}
return obj; return obj;
} }
@ -1037,7 +1033,7 @@
if (Object.keys(diff.updated).length !== 0) if (Object.keys(diff.updated).length !== 0)
obj = mergeRecursive(obj, diff.updated) obj = mergeRecursive(obj, diff.updated)
if (Object.keys(diff.deleted).length !== 0) { if (Object.keys(diff.deleted).length !== 0) {
obj = mergeRecursive(obj, diff.deleted) obj = mergeRecursive(obj, diff.deleted, true)
obj = cleanse(obj) obj = cleanse(obj)
} }
if (Object.keys(diff.added).length !== 0) 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 */ /* FLO Crypto Operators */
'use strict'; 'use strict';
const floCrypto = EXPORTS; const floCrypto = EXPORTS;
@ -276,12 +276,18 @@
} }
//Convert the given address (any blockchain) to equivalent floID //Convert the given address (any blockchain) to equivalent floID
floCrypto.toFloID = function(address) { floCrypto.toFloID = function (address, options = null) {
if (!address) if (!address)
return; return;
let raw = decodeAddress(address); let raw = decodeAddress(address);
if (!raw) if (!raw)
return; 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); raw.bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, { let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
asBytes: true asBytes: true

View File

@ -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) => { return new Promise(async (resolve, reject) => {
let decode = coinjs.script().decodeRedeemScript(redeemScript); let decode = coinjs.script().decodeRedeemScript(redeemScript);
if (!decode || decode.address !== address || decode.type !== "multisig__") if (!decode || decode.address !== address || decode.type !== "multisig__")
@ -1137,10 +1137,10 @@
return reject("Invalid multisig (required is greater than users)"); return reject("Invalid multisig (required is greater than users)");
let co_owners = decode.pubkeys.map(p => floCrypto.getFloID(p)); let co_owners = decode.pubkeys.map(p => floCrypto.getFloID(p));
let privateKey = await floDapps.user.private; let privateKey = await floDapps.user.private;
btcOperator.createMultiSigTx(address, redeemScript, receivers, amounts, fee).then(tx => { btcOperator.createMultiSigTx(address, redeemScript, receivers, amounts, fee, options).then(({ tx_hex }) => {
tx = btcOperator.signTx(tx, privateKey); tx_hex = btcOperator.signTx(tx_hex, privateKey);
createPipeline(TYPE_BTC_MULTISIG, co_owners, 32).then(pipeline => { 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) sendRaw(message, pipeline.id, "TRANSACTION", false)
.then(result => resolve(pipeline.id)) .then(result => resolve(pipeline.id))
.catch(error => reject(error)) .catch(error => reject(error))
@ -1165,8 +1165,7 @@
tx_hex: tx_hex_signed tx_hex: tx_hex_signed
}); });
debugger; debugger;
btcOperator.broadcast(tx_hex_signed).then(result => { btcOperator.broadcastTx(tx_hex_signed).then(txid => {
let txid = result.txid;
console.debug(txid); console.debug(txid);
sendRaw(encrypt(txid, pipeline.eKey), pipeline.id, "BROADCAST", false) sendRaw(encrypt(txid, pipeline.eKey), pipeline.id, "BROADCAST", false)
.then(result => resolve({ .then(result => resolve({