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:
parent
59b0744b3f
commit
1e9c414bad
@ -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))
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)));
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user