Feature update

-- added feature to open article correctly even if not logged in
This commit is contained in:
sairaj mote 2023-03-06 01:42:44 +05:30
parent 5ae03dc85f
commit 2d4741fdec
5 changed files with 955 additions and 149 deletions

View File

@ -882,7 +882,7 @@
} }
} }
window.addEventListener('hashchange', e => showPage(window.location.hash)) window.addEventListener('hashchange', e => routeTo(window.location.hash))
window.addEventListener("load", () => { window.addEventListener("load", () => {
document.body.classList.remove('hidden') document.body.classList.remove('hidden')
document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex) document.querySelectorAll('sm-input[data-private-key]').forEach(input => input.customValidation = floCrypto.getPubKeyHex)
@ -937,8 +937,8 @@
params: {} params: {}
} }
async function showPage(targetPage, options = {}) { async function routeTo(targetPage, options = {}) {
const { firstLoad, hashChange, isPreview } = options const { firstLoad, hashChange, isPreview, redirected } = options
let pageId let pageId
let params = {} let params = {}
let searchParams let searchParams
@ -968,6 +968,9 @@
if (isPreview) { if (isPreview) {
floGlobals.preview = location.hash; floGlobals.preview = location.hash;
} }
if (redirected) {
floGlobals.redirectToArticle = location.hash
}
if (typeof floGlobals.myFloID === "undefined" && !(['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) return if (typeof floGlobals.myFloID === "undefined" && !(['sign_up', 'sign_in', 'loading', 'landing'].includes(pageId))) return
if (searchParams) { if (searchParams) {
const urlSearchParams = new URLSearchParams('?' + searchParams); const urlSearchParams = new URLSearchParams('?' + searchParams);
@ -986,6 +989,7 @@
floCloudAPI.requestGeneralData(`${params.articleID}_gd`) floCloudAPI.requestGeneralData(`${params.articleID}_gd`)
]) ])
} }
console.log(params)
switch (pageId) { switch (pageId) {
case 'landing': case 'landing':
targetPage = 'landing' targetPage = 'landing'
@ -2721,19 +2725,19 @@
function getSignedIn() { function getSignedIn() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) { if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) {
showPage(window.location.hash) routeTo(window.location.hash)
} else { } else {
showPage('landing', { isPreview: location.hash.includes('preview') }) routeTo('landing', { isPreview: location.hash.includes('preview'), redirected: location.hash.includes('articleID') })
} }
getRef('sign_in_button').onclick = () => { getRef('sign_in_button').onclick = () => {
resolve(getRef('private_key_field').value.trim()) resolve(getRef('private_key_field').value.trim())
getRef('private_key_field').value = '' getRef('private_key_field').value = ''
showPage('loading') routeTo('loading')
} }
getRef('sign_up_button').onclick = () => { getRef('sign_up_button').onclick = () => {
resolve(getRef('keys_generator').keys.privKey); resolve(getRef('keys_generator').keys.privKey);
getRef('keys_generator').clearKeys(); getRef('keys_generator').clearKeys();
showPage('loading') routeTo('loading')
} }
}) })
} }
@ -2824,10 +2828,10 @@
</sm-button> </sm-button>
` `
if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) { if (window.location.hash.includes('sign_in') || window.location.hash.includes('sign_up')) {
const hash = floGlobals.preview ? floGlobals.preview : ' ' const hash = floGlobals.preview || floGlobals.redirectToArticle || ' '
history.replaceState(null, null, hash) history.replaceState(null, null, hash)
} }
showPage(window.location.hash, { firstLoad: true }) routeTo(window.location.hash, { firstLoad: true })
console.log(result) console.log(result)
}).catch(error => console.error(error)) }).catch(error => console.error(error))
} }

View File

@ -1,29 +1,40 @@
(function (EXPORTS) { //btcOperator v1.0.14b (function (EXPORTS) { //btcOperator v1.1.2a
/* BTC Crypto and API Operator */ /* BTC Crypto and API Operator */
const btcOperator = EXPORTS; const btcOperator = EXPORTS;
//This library uses API provided by chain.so (https://chain.so/) //This library uses API provided by chain.so (https://chain.so/)
const URL = "https://chain.so/api/v2/"; const URL = "https://blockchain.info/";
const fetch_api = btcOperator.fetch = function (api) { const fetch_api = btcOperator.fetch = function (api, json_res = true) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
console.debug(URL + api); console.debug(URL + api);
fetch(URL + api).then(response => { fetch(URL + api).then(response => {
response.json() if (response.ok) {
.then(result => result.status === "success" ? resolve(result) : reject(result)) (json_res ? response.json() : response.text())
.catch(error => reject(error)) .then(result => resolve(result))
.catch(error => reject(error))
} else {
response.json()
.then(result => reject(result))
.catch(error => reject(error))
}
}).catch(error => reject(error)) }).catch(error => reject(error))
}) })
}; };
const SATOSHI_IN_BTC = 1e8; const SATOSHI_IN_BTC = 1e8;
const util = btcOperator.util = {};
util.Sat_to_BTC = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
util.BTC_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
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(parseFloat((result.regular / SATOSHI_IN_BTC).toFixed(8)))) .then(result => resolve(util.Sat_to_BTC(result.regular)))
.catch(error => reject(error)); .catch(error => reject(error));
else else
reject(response); reject(response);
@ -119,50 +130,84 @@
return coinjs.pubkeys2MultisigAddress(pubKeys, minRequired); return coinjs.pubkeys2MultisigAddress(pubKeys, minRequired);
} }
btcOperator.decodeRedeemScript = function (redeemScript, bech32 = true) {
let script = coinjs.script();
let decoded = (bech32) ?
script.decodeRedeemScriptBech32(redeemScript) :
script.decodeRedeemScript(redeemScript);
if (!decoded)
return null;
return {
address: decoded.address,
pubKeys: decoded.pubkeys,
redeemScript: decoded.redeemscript,
required: decoded.signaturesRequired
}
}
//convert from one blockchain to another blockchain (target version) //convert from one blockchain to another blockchain (target version)
btcOperator.convert = {}; btcOperator.convert = {};
btcOperator.convert.wif = function (source_wif, target_version = coinjs.priv) { btcOperator.convert.wif = function (source_wif, target_version = coinjs.priv) {
let keyHex = decodeLegacy(source_wif).hex; let keyHex = util.decodeLegacy(source_wif).hex;
if (!keyHex || keyHex.length < 66 || !/01$/.test(keyHex)) if (!keyHex || keyHex.length < 66 || !/01$/.test(keyHex))
return null; return null;
else else
return encodeLegacy(keyHex, target_version); return util.encodeLegacy(keyHex, target_version);
} }
btcOperator.convert.legacy2legacy = function (source_addr, target_version = coinjs.pub) { btcOperator.convert.legacy2legacy = function (source_addr, target_version = coinjs.pub) {
let rawHex = decodeLegacy(source_addr).hex; let rawHex = util.decodeLegacy(source_addr).hex;
if (!rawHex) if (!rawHex)
return null; return null;
else else
return encodeLegacy(rawHex, target_version); return util.encodeLegacy(rawHex, target_version);
} }
btcOperator.convert.legacy2bech = function (source_addr, target_version = coinjs.bech32.version, target_hrp = coinjs.bech32.hrp) { btcOperator.convert.legacy2bech = function (source_addr, target_version = coinjs.bech32.version, target_hrp = coinjs.bech32.hrp) {
let rawHex = decodeLegacy(source_addr).hex; let rawHex = util.decodeLegacy(source_addr).hex;
if (!rawHex) if (!rawHex)
return null; return null;
else else
return encodeBech32(rawHex, target_version, target_hrp); return util.encodeBech32(rawHex, target_version, target_hrp);
} }
btcOperator.convert.bech2bech = function (source_addr, target_version = coinjs.bech32.version, target_hrp = coinjs.bech32.hrp) { btcOperator.convert.bech2bech = function (source_addr, target_version = coinjs.bech32.version, target_hrp = coinjs.bech32.hrp) {
let rawHex = decodeBech32(source_addr).hex; let rawHex = util.decodeBech32(source_addr).hex;
if (!rawHex) if (!rawHex)
return null; return null;
else else
return encodeBech32(rawHex, target_version, target_hrp); return util.encodeBech32(rawHex, target_version, target_hrp);
} }
btcOperator.convert.bech2legacy = function (source_addr, target_version = coinjs.pub) { btcOperator.convert.bech2legacy = function (source_addr, target_version = coinjs.pub) {
let rawHex = decodeBech32(source_addr).hex; let rawHex = util.decodeBech32(source_addr).hex;
if (!rawHex) if (!rawHex)
return null; return null;
else else
return encodeLegacy(rawHex, target_version); return util.encodeLegacy(rawHex, target_version);
} }
function decodeLegacy(source) { btcOperator.convert.multisig2multisig = function (source_addr, target_version = coinjs.multisig) {
let rawHex = util.decodeLegacy(source_addr).hex;
if (!rawHex)
return null;
else
return util.encodeLegacy(rawHex, target_version);
}
btcOperator.convert.bech2multisig = function (source_addr, target_version = coinjs.multisig) {
let rawHex = util.decodeBech32(source_addr).hex;
if (!rawHex)
return null;
else {
rawHex = Crypto.util.bytesToHex(ripemd160(Crypto.util.hexToBytes(rawHex), { asBytes: true }));
return util.encodeLegacy(rawHex, target_version);
}
}
util.decodeLegacy = function (source) {
var decode = coinjs.base58decode(source); var decode = coinjs.base58decode(source);
var raw = decode.slice(0, decode.length - 4), var raw = decode.slice(0, decode.length - 4),
checksum = decode.slice(decode.length - 4); checksum = decode.slice(decode.length - 4);
@ -172,7 +217,7 @@
asBytes: true asBytes: true
}); });
if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3]) if (hash[0] != checksum[0] || hash[1] != checksum[1] || hash[2] != checksum[2] || hash[3] != checksum[3])
return null; return false;
let version = raw.shift(); let version = raw.shift();
return { return {
version: version, version: version,
@ -180,7 +225,7 @@
} }
} }
function encodeLegacy(hex, version) { util.encodeLegacy = function (hex, version) {
var bytes = Crypto.util.hexToBytes(hex); var bytes = Crypto.util.hexToBytes(hex);
bytes.unshift(version); bytes.unshift(version);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, { var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
@ -192,10 +237,10 @@
return coinjs.base58encode(bytes.concat(checksum)); return coinjs.base58encode(bytes.concat(checksum));
} }
function decodeBech32(source) { util.decodeBech32 = function (source) {
let decode = coinjs.bech32_decode(source); let decode = coinjs.bech32_decode(source);
if (!decode) if (!decode)
return null; return false;
var raw = decode.data; var raw = decode.data;
let version = raw.shift(); let version = raw.shift();
raw = coinjs.bech32_convert(raw, 5, 8, false); raw = coinjs.bech32_convert(raw, 5, 8, false);
@ -206,7 +251,7 @@
} }
} }
function encodeBech32(hex, version, hrp) { util.encodeBech32 = function (hex, version, hrp) {
var bytes = Crypto.util.hexToBytes(hex); var bytes = Crypto.util.hexToBytes(hex);
bytes = coinjs.bech32_convert(bytes, 8, 5, true); bytes = coinjs.bech32_convert(bytes, 8, 5, true);
bytes.unshift(version) bytes.unshift(version)
@ -216,8 +261,8 @@
//BTC blockchain APIs //BTC blockchain APIs
btcOperator.getBalance = addr => new Promise((resolve, reject) => { btcOperator.getBalance = addr => new Promise((resolve, reject) => {
fetch_api(`get_address_balance/BTC/${addr}`) fetch_api(`q/addressbalance/${addr}`)
.then(result => resolve(parseFloat(result.data.confirmed_balance))) .then(result => resolve(util.Sat_to_BTC(result)))
.catch(error => reject(error)) .catch(error => reject(error))
}); });
@ -308,7 +353,7 @@
parameters.privkeys[i] = coinjs.privkey2wif(key); parameters.privkeys[i] = coinjs.privkey2wif(key);
}); });
if (invalids.length) if (invalids.length)
throw "Invalid keys:" + invalids; throw "Invalid private key for address:" + invalids;
} }
//receiver-ids (and change-id) //receiver-ids (and change-id)
if (!Array.isArray(parameters.receivers)) if (!Array.isArray(parameters.receivers))
@ -339,9 +384,9 @@
let output_size = addOutputs(tx, receivers, amounts, change_address); let output_size = addOutputs(tx, receivers, amounts, change_address);
addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver).then(result => { addInputs(tx, senders, redeemScripts, total_amount, fee, output_size, fee_from_receiver).then(result => {
if (result.change_amount > 0 && result.change_amount > result.fee) //add change amount if any (ignore dust change) if (result.change_amount > 0 && result.change_amount > result.fee) //add change amount if any (ignore dust change)
tx.outs[tx.outs.length - 1].value = parseInt(result.change_amount * SATOSHI_IN_BTC); //values are in satoshi tx.outs[tx.outs.length - 1].value = util.BTC_to_Sat(result.change_amount); //values are in satoshi
if (fee_from_receiver) { //deduce fee from receivers if fee_from_receiver if (fee_from_receiver) { //deduce fee from receivers if fee_from_receiver
let fee_remaining = parseInt(result.fee * SATOSHI_IN_BTC); let fee_remaining = util.BTC_to_Sat(result.fee);
for (let i = 0; i < tx.outs.length - 1 && fee_remaining > 0; i++) { for (let i = 0; i < tx.outs.length - 1 && fee_remaining > 0; i++) {
if (fee_remaining < tx.outs[i].value) { if (fee_remaining < tx.outs[i].value) {
tx.outs[i].value -= fee_remaining; tx.outs[i].value -= fee_remaining;
@ -409,29 +454,29 @@
rs = redeemScripts[rec_args.n]; rs = redeemScripts[rec_args.n];
let addr_type = coinjs.addressDecode(addr).type; let addr_type = coinjs.addressDecode(addr).type;
let size_per_input = _sizePerInput(addr, rs); let size_per_input = _sizePerInput(addr, rs);
fetch_api(`get_tx_unspent/BTC/${addr}`).then(result => { fetch_api(`unspent?active=${addr}`).then(result => {
let utxos = result.data.txs; let utxos = result.unspent_outputs;
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;
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;
else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi)) || addr_type === 'multisigBech32') { else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi)) || addr_type === 'multisigBech32') {
//redeemScript for segwit/bech32 and multisig (bech32) //redeemScript for segwit/bech32 and multisig (bech32)
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 * SATOSHI_IN_BTC).toFixed(0), 8)); s.writeBytes(coinjs.numToBytes(utxos[i].value.toFixed(0), 8));
script = Crypto.util.bytesToHex(s.buffer); script = Crypto.util.bytesToHex(s.buffer);
} else //redeemScript for multisig (segwit) } else //redeemScript for multisig (segwit)
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].tx_hash_big_endian, utxos[i].tx_output_n, script, 0xfffffffd /*sequence*/); //0xfffffffd for Replace-by-fee
//update track values //update track values
rec_args.input_size += size_per_input; rec_args.input_size += size_per_input;
rec_args.input_amount += parseFloat(utxos[i].value); rec_args.input_amount += util.Sat_to_BTC(utxos[i].value);
required_amount -= parseFloat(utxos[i].value); required_amount -= util.Sat_to_BTC(utxos[i].value);
if (fee_rate) //automatic fee calculation (dynamic) if (fee_rate) //automatic fee calculation (dynamic)
required_amount += size_per_input * fee_rate; required_amount += size_per_input * fee_rate;
} }
@ -658,20 +703,23 @@
btcOperator.checkIfSameTx = function (tx1, tx2) { btcOperator.checkIfSameTx = function (tx1, tx2) {
tx1 = deserializeTx(tx1); tx1 = deserializeTx(tx1);
tx2 = deserializeTx(tx2); tx2 = deserializeTx(tx2);
//compare input and output length
if (tx1.ins.length !== tx2.ins.length || tx1.outs.length !== tx2.outs.length) if (tx1.ins.length !== tx2.ins.length || tx1.outs.length !== tx2.outs.length)
return false; return false;
//compare inputs
for (let i = 0; i < tx1.ins.length; i++) for (let i = 0; i < tx1.ins.length; i++)
if (tx1.ins[i].outpoint.hash !== tx2.ins[i].outpoint.hash || tx1.ins[i].outpoint.index !== tx2.ins[i].outpoint.index) if (tx1.ins[i].outpoint.hash !== tx2.ins[i].outpoint.hash || tx1.ins[i].outpoint.index !== tx2.ins[i].outpoint.index)
return false; return false;
for (let i = 0; i < tx2.ins.length; i++) //compare outputs
for (let i = 0; i < tx1.outs.length; i++)
if (tx1.outs[i].value !== tx2.outs[i].value || Crypto.util.bytesToHex(tx1.outs[i].script.buffer) !== Crypto.util.bytesToHex(tx2.outs[i].script.buffer)) if (tx1.outs[i].value !== tx2.outs[i].value || Crypto.util.bytesToHex(tx1.outs[i].script.buffer) !== Crypto.util.bytesToHex(tx2.outs[i].script.buffer))
return false; return false;
return true; return true;
} }
const getTxOutput = (txid, i) => new Promise((resolve, reject) => { const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
fetch_api(`get_tx_outputs/BTC/${txid}/${i}`) fetch_api(`rawtx/${txid}`)
.then(result => resolve(result.data.outputs)) .then(result => resolve(result.out[i]))
.catch(error => reject(error)) .catch(error => reject(error))
}); });
@ -685,8 +733,8 @@
promises.push(getTxOutput(tx.ins[i].outpoint.hash, tx.ins[i].outpoint.index)); promises.push(getTxOutput(tx.ins[i].outpoint.hash, tx.ins[i].outpoint.index));
Promise.all(promises).then(inputs => { Promise.all(promises).then(inputs => {
result.inputs = inputs.map(inp => Object({ result.inputs = inputs.map(inp => Object({
address: inp.address, address: inp.addr,
value: parseFloat(inp.value) value: util.Sat_to_BTC(inp.value)
})); }));
let signed = checkSigned(tx, false); let signed = checkSigned(tx, false);
result.inputs.forEach((inp, i) => inp.signed = signed[i]); result.inputs.forEach((inp, i) => inp.signed = signed[i]);
@ -695,17 +743,17 @@
var address; var address;
switch (out.script.chunks[0]) { switch (out.script.chunks[0]) {
case 0: //bech32, multisig-bech32 case 0: //bech32, multisig-bech32
address = encodeBech32(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.bech32.version, coinjs.bech32.hrp); address = util.encodeBech32(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.bech32.version, coinjs.bech32.hrp);
break; break;
case 169: //segwit, multisig-segwit case 169: //segwit, multisig-segwit
address = encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.multisig); address = util.encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[1]), coinjs.multisig);
break; break;
case 118: //legacy case 118: //legacy
address = encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[2]), coinjs.pub); address = util.encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[2]), coinjs.pub);
} }
return { return {
address, address,
value: parseFloat(out.value / SATOSHI_IN_BTC) value: util.Sat_to_BTC(out.value)
} }
}); });
//Parse Totals //Parse Totals
@ -726,22 +774,115 @@
return Crypto.util.bytesToHex(txid); return Crypto.util.bytesToHex(txid);
} }
btcOperator.getTx = txid => new Promise((resolve, reject) => { const getLatestBlock = btcOperator.getLatestBlock = () => new Promise((resolve, reject) => {
fetch_api(`get_tx/BTC/${txid}`) fetch_api(`q/getblockcount`)
.then(result => resolve(result.data)) .then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
})
btcOperator.getTx = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}`).then(result => {
getLatestBlock().then(latest_block => resolve({
block: result.block_height,
txid: result.hash,
time: result.time * 1000,
confirmations: result.block_height === null ? 0 : latest_block - result.block_height, //calculate confirmations using latest block number as api doesnt relay it
size: result.size,
fee: util.Sat_to_BTC(result.fee),
inputs: result.inputs.map(i => Object({ address: i.prev_out.addr, value: util.Sat_to_BTC(i.prev_out.value) })),
total_input_value: util.Sat_to_BTC(result.inputs.reduce((a, i) => a + i.prev_out.value, 0)),
outputs: result.out.map(o => Object({ address: o.addr, value: util.Sat_to_BTC(o.value) })),
total_output_value: util.Sat_to_BTC(result.out.reduce((a, o) => a += o.value, 0)),
}))
}).catch(error => reject(error))
}); });
btcOperator.getAddressData = addr => new Promise((resolve, reject) => { btcOperator.getTx.hex = txid => new Promise((resolve, reject) => {
fetch_api(`address/BTC/${addr}`) fetch_api(`rawtx/${txid}?format=hex`, false)
.then(result => resolve(result.data)) .then(result => resolve(result))
.catch(error => reject(error)) .catch(error => reject(error))
})
btcOperator.getAddressData = address => new Promise((resolve, reject) => {
fetch_api(`rawaddr/${address}`).then(data => {
let details = {};
details.balance = util.Sat_to_BTC(data.final_balance);
details.address = data.address;
details.txs = data.txs.map(tx => {
let d = {
txid: tx.hash,
time: tx.time * 1000, //s to ms
block: tx.block_height,
}
//sender list
d.tx_senders = {};
tx.inputs.forEach(i => {
if (i.prev_out.addr in d.tx_senders)
d.tx_senders[i.prev_out.addr] += i.prev_out.value;
else d.tx_senders[i.prev_out.addr] = i.prev_out.value;
});
d.tx_input_value = 0;
for (let s in d.tx_senders) {
let val = d.tx_senders[s];
d.tx_senders[s] = util.Sat_to_BTC(val);
d.tx_input_value += val;
}
d.tx_input_value = util.Sat_to_BTC(d.tx_input_value);
//receiver list
d.tx_receivers = {};
tx.out.forEach(o => {
if (o.addr in d.tx_receivers)
d.tx_receivers[o.addr] += o.value;
else d.tx_receivers[o.addr] = o.value;
});
d.tx_output_value = 0;
for (let r in d.tx_receivers) {
let val = d.tx_receivers[r];
d.tx_receivers[r] = util.Sat_to_BTC(val);
d.tx_output_value += val;
}
d.tx_output_value = util.Sat_to_BTC(d.tx_output_value);
d.tx_fee = util.Sat_to_BTC(tx.fee);
//tx type
if (tx.result > 0) { //net > 0, balance inc => type=in
d.type = "in";
d.amount = util.Sat_to_BTC(tx.result);
d.sender = Object.keys(d.tx_senders).filter(s => s !== address);
} else if (Object.keys(d.tx_receivers).some(r => r !== address)) { //net < 0, balance dec & receiver present => type=out
d.type = "out";
d.amount = util.Sat_to_BTC(tx.result * -1);
d.receiver = Object.keys(d.tx_receivers).filter(r => r !== address);
d.fee = d.tx_fee;
} else { //net < 0 (fee) & no other id in receiver list => type=self
d.type = "self";
d.amount = d.tx_receivers[address];
d.address = address
}
return d;
})
resolve(details);
}).catch(error => reject(error))
}); });
btcOperator.getBlock = block => new Promise((resolve, reject) => { btcOperator.getBlock = block => new Promise((resolve, reject) => {
fetch_api(`get_block/BTC/${block}`) fetch_api(`rawblock/${block}`).then(result => resolve({
.then(result => resolve(result.data)) height: result.height,
.catch(error => reject(error)) hash: result.hash,
merkle_root: result.mrkl_root,
prev_block: result.prev_block,
next_block: result.next_block[0],
size: result.size,
time: result.time * 1000, //s to ms
txs: result.tx.map(t => Object({
fee: t.fee,
size: t.size,
inputs: t.inputs.map(i => Object({ address: i.prev_out.addr, value: util.Sat_to_BTC(i.prev_out.value) })),
total_input_value: util.Sat_to_BTC(t.inputs.reduce((a, i) => a + i.prev_out.value, 0)),
outputs: t.out.map(o => Object({ address: o.addr, value: util.Sat_to_BTC(o.value) })),
total_output_value: util.Sat_to_BTC(t.out.reduce((a, o) => a += o.value, 0)),
}))
})).catch(error => reject(error))
}); });
})('object' === typeof module ? module.exports : window.btcOperator = {}); })('object' === typeof module ? module.exports : window.btcOperator = {});

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //floBlockchainAPI v2.3.3e (function (EXPORTS) { //floBlockchainAPI v2.4.3
/* 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;
@ -15,6 +15,13 @@
receiverID: floGlobals.adminID receiverID: floGlobals.adminID
}; };
const SATOSHI_IN_BTC = 1e8;
const util = floBlockchainAPI.util = {};
util.Sat_to_FLO = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
util.FLO_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
Object.defineProperties(floBlockchainAPI, { Object.defineProperties(floBlockchainAPI, {
sendAmt: { sendAmt: {
get: () => DEFAULT.sendAmt, get: () => DEFAULT.sendAmt,
@ -121,17 +128,15 @@
}); });
} }
//Send Tx to blockchain //create a transaction with single sender
const sendTx = floBlockchainAPI.sendTx = function (senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) { const createTx = function (senderAddr, receiverAddr, sendAmt, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
if (!floCrypto.validateASCII(floData)) if (!floCrypto.validateASCII(floData))
return reject("Invalid FLO_Data: only printable ASCII characters are allowed"); return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
else if (!floCrypto.validateFloID(senderAddr)) else if (!floCrypto.validateFloID(senderAddr, true))
return reject(`Invalid address : ${senderAddr}`); return reject(`Invalid address : ${senderAddr}`);
else if (!floCrypto.validateFloID(receiverAddr)) else if (!floCrypto.validateFloID(receiverAddr))
return reject(`Invalid address : ${receiverAddr}`); return reject(`Invalid address : ${receiverAddr}`);
else if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr))
return reject("Invalid Private key!");
else if (typeof sendAmt !== 'number' || sendAmt <= 0) else if (typeof sendAmt !== 'number' || sendAmt <= 0)
return reject(`Invalid sendAmt : ${sendAmt}`); return reject(`Invalid sendAmt : ${sendAmt}`);
@ -175,15 +180,36 @@
if (change > DEFAULT.minChangeAmt) 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); resolve(trx);
broadcastTx(signedTxHash)
.then(txid => resolve(txid))
.catch(error => reject(error))
} }
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
})
}
floBlockchainAPI.createTx = function (senderAddr, receiverAddr, sendAmt, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => {
createTx(senderAddr, receiverAddr, sendAmt, floData, strict_utxo)
.then(trx => resolve(trx.serialize()))
.catch(error => reject(error))
})
}
//Send Tx to blockchain
const sendTx = floBlockchainAPI.sendTx = function (senderAddr, receiverAddr, sendAmt, privKey, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => {
if (!floCrypto.validateFloID(senderAddr, true))
return reject(`Invalid address : ${senderAddr}`);
else if (privKey.length < 1 || !floCrypto.verifyPrivKey(privKey, senderAddr))
return reject("Invalid Private key!");
createTx(senderAddr, receiverAddr, sendAmt, floData, strict_utxo).then(trx => {
var signedTxHash = trx.sign(privKey, 1);
broadcastTx(signedTxHash)
.then(txid => resolve(txid))
.catch(error => reject(error))
}).catch(error => reject(error))
}); });
} }
@ -203,7 +229,7 @@
//merge all UTXOs of a given floID into a single UTXO //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) => { return new Promise((resolve, reject) => {
if (!floCrypto.validateFloID(floID)) if (!floCrypto.validateFloID(floID, true))
return reject(`Invalid floID`); return reject(`Invalid floID`);
if (!floCrypto.verifyPrivKey(privKey, floID)) if (!floCrypto.verifyPrivKey(privKey, floID))
return reject("Invalid Private Key"); return reject("Invalid Private Key");
@ -381,7 +407,6 @@
for (let floID in senders) for (let floID in senders)
promises.push(promisedAPI(`api/addr/${floID}/utxo`)); promises.push(promisedAPI(`api/addr/${floID}/utxo`));
Promise.all(promises).then(results => { Promise.all(promises).then(results => {
let wifSeq = [];
var trx = bitjs.transaction(); var trx = bitjs.transaction();
for (let floID in senders) { for (let floID in senders) {
let utxos = results.shift(); let utxos = results.shift();
@ -391,13 +416,11 @@
sendAmt = totalSendAmt * ratio; sendAmt = totalSendAmt * ratio;
} else } else
sendAmt = senders[floID].coins + dividedFee; sendAmt = senders[floID].coins + dividedFee;
let wif = senders[floID].wif;
let utxoAmt = 0.0; let utxoAmt = 0.0;
for (let i = utxos.length - 1; for (let i = utxos.length - 1;
(i >= 0) && (utxoAmt < sendAmt); i--) { (i >= 0) && (utxoAmt < sendAmt); i--) {
if (utxos[i].confirmations) { if (utxos[i].confirmations) {
trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey); trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey);
wifSeq.push(wif);
utxoAmt += utxos[i].amount; utxoAmt += utxos[i].amount;
} }
} }
@ -410,8 +433,8 @@
for (let floID in receivers) for (let floID in receivers)
trx.addoutput(floID, receivers[floID]); trx.addoutput(floID, receivers[floID]);
trx.addflodata(floData.replace(/\n/g, ' ')); trx.addflodata(floData.replace(/\n/g, ' '));
for (let i = 0; i < wifSeq.length; i++) for (let floID in senders)
trx.signinput(i, wifSeq[i], 1); trx.sign(senders[floID].wif, 1);
var signedTxHash = trx.serialize(); var signedTxHash = trx.serialize();
broadcastTx(signedTxHash) broadcastTx(signedTxHash)
.then(txid => resolve(txid)) .then(txid => resolve(txid))
@ -421,6 +444,273 @@
}) })
} }
//Create a multisig transaction
const createMultisigTx = function (redeemScript, receivers, amounts, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => {
var multisig = floCrypto.decodeRedeemScript(redeemScript);
//validate multisig script and flodata
if (!multisig)
return reject(`Invalid redeemScript`);
var senderAddr = multisig.address;
if (!floCrypto.validateFloID(senderAddr))
return reject(`Invalid multisig : ${senderAddr}`);
else if (!floCrypto.validateASCII(floData))
return reject("Invalid FLO_Data: only printable ASCII characters are allowed");
//validate receiver addresses
if (!Array.isArray(receivers))
receivers = [receivers];
for (let r of receivers)
if (!floCrypto.validateFloID(r))
return reject(`Invalid address : ${r}`);
//validate amounts
if (!Array.isArray(amounts))
amounts = [amounts];
if (amounts.length != receivers.length)
return reject("Receivers and amounts have different length");
var sendAmt = 0;
for (let a of amounts) {
if (typeof a !== 'number' || a <= 0)
return reject(`Invalid amount : ${a}`);
sendAmt += a;
}
getBalance(senderAddr).then(balance => {
var fee = DEFAULT.fee;
if (balance < sendAmt + fee)
return reject("Insufficient FLO balance!");
//get unconfirmed tx list
promisedAPI(`api/addr/${senderAddr}`).then(result => {
readTxs(senderAddr, 0, result.unconfirmedTxApperances).then(result => {
let unconfirmedSpent = {};
for (let tx of result.items)
if (tx.confirmations == 0)
for (let vin of tx.vin)
if (vin.addr === senderAddr) {
if (Array.isArray(unconfirmedSpent[vin.txid]))
unconfirmedSpent[vin.txid].push(vin.vout);
else
unconfirmedSpent[vin.txid] = [vin.vout];
}
//get utxos list
promisedAPI(`api/addr/${senderAddr}/utxo`).then(utxos => {
//form/construct the transaction data
var trx = bitjs.transaction();
var utxoAmt = 0.0;
for (var i = utxos.length - 1;
(i >= 0) && (utxoAmt < sendAmt + fee); i--) {
//use only utxos with confirmations (strict_utxo mode)
if (utxos[i].confirmations || !strict_utxo) {
if (utxos[i].txid in unconfirmedSpent && unconfirmedSpent[utxos[i].txid].includes(utxos[i].vout))
continue; //A transaction has already used the utxo, but is unconfirmed.
trx.addinput(utxos[i].txid, utxos[i].vout, redeemScript); //for multisig, script=redeemScript
utxoAmt += utxos[i].amount;
};
}
if (utxoAmt < sendAmt + fee)
reject("Insufficient FLO: Some UTXOs are unconfirmed");
else {
for (let i in receivers)
trx.addoutput(receivers[i], amounts[i]);
var change = utxoAmt - sendAmt - fee;
if (change > DEFAULT.minChangeAmt)
trx.addoutput(senderAddr, change);
trx.addflodata(floData.replace(/\n/g, ' '));
resolve(trx);
}
}).catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
});
}
//Same as above, but explict call should return serialized tx-hex
floBlockchainAPI.createMultisigTx = function (redeemScript, receivers, amounts, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => {
createMultisigTx(redeemScript, receivers, amounts, floData, strict_utxo)
.then(trx => resolve(trx.serialize()))
.catch(error => reject(error))
})
}
//Create and send multisig transaction
const sendMultisigTx = floBlockchainAPI.sendMultisigTx = function (redeemScript, privateKeys, receivers, amounts, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => {
var multisig = floCrypto.decodeRedeemScript(redeemScript);
if (!multisig)
return reject(`Invalid redeemScript`);
if (privateKeys.length < multisig.required)
return reject(`Insufficient privateKeys (required ${multisig.required})`);
for (let pk of privateKeys) {
var flag = false;
for (let pub of multisig.pubkeys)
if (floCrypto.verifyPrivKey(pk, pub, false))
flag = true;
if (!flag)
return reject(`Invalid Private key`);
}
createMultisigTx(redeemScript, receivers, amounts, floData, strict_utxo).then(trx => {
for (let pk of privateKeys)
trx.sign(pk, 1);
var signedTxHash = trx.serialize();
broadcastTx(signedTxHash)
.then(txid => resolve(txid))
.catch(error => reject(error))
}).catch(error => reject(error))
})
}
floBlockchainAPI.writeMultisigData = function (redeemScript, data, privatekeys, 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) => {
if (!floCrypto.validateFloID(receiverAddr))
return reject(`Invalid receiver: ${receiverAddr}`);
sendMultisigTx(redeemScript, privatekeys, receiverAddr, sendAmt, data, strict_utxo)
.then(txid => resolve(txid))
.catch(error => reject(error))
})
}
function deserializeTx(tx) {
if (typeof tx === 'string' || Array.isArray(tx)) {
try {
tx = bitjs.transaction(tx);
} catch {
throw "Invalid transaction hex";
}
} else if (typeof tx !== 'object' || typeof tx.sign !== 'function')
throw "Invalid transaction object";
return tx;
}
floBlockchainAPI.signTx = function (tx, privateKey, sighashtype = 1) {
if (!floCrypto.getFloID(privateKey))
throw "Invalid Private key";
//deserialize if needed
tx = deserializeTx(tx);
var signedTxHex = tx.sign(privateKey, sighashtype);
return signedTxHex;
}
const checkSigned = floBlockchainAPI.checkSigned = function (tx, bool = true) {
tx = deserializeTx(tx);
let n = [];
for (let i = 0; i < tx.inputs.length; i++) {
var s = tx.scriptDecode(i);
if (s['type'] === 'scriptpubkey')
n.push(s.signed);
else if (s['type'] === 'multisig') {
var rs = tx.decodeRedeemScript(s['rs']);
let x = {
s: 0,
r: rs['required'],
t: rs['pubkeys'].length
};
//check input script for signatures
var script = Array.from(tx.inputs[i].script);
if (script[0] == 0) { //script with signatures
script = tx.parseScript(script);
for (var k = 0; k < script.length; k++)
if (Array.isArray(script[k]) && script[k][0] == 48) //0x30 DERSequence
x.s++;
}
//validate counts
if (x.r > x.t)
throw "signaturesRequired is more than publicKeys";
else if (x.s < x.r)
n.push(x);
else
n.push(true);
}
}
return bool ? !(n.filter(x => x !== true).length) : n;
}
floBlockchainAPI.checkIfSameTx = function (tx1, tx2) {
tx1 = deserializeTx(tx1);
tx2 = deserializeTx(tx2);
//compare input and output length
if (tx1.inputs.length !== tx2.inputs.length || tx1.outputs.length !== tx2.outputs.length)
return false;
//compare flodata
if (tx1.floData !== tx2.floData)
return false
//compare inputs
for (let i = 0; i < tx1.inputs.length; i++)
if (tx1.inputs[i].outpoint.hash !== tx2.inputs[i].outpoint.hash || tx1.inputs[i].outpoint.index !== tx2.inputs[i].outpoint.index)
return false;
//compare outputs
for (let i = 0; i < tx1.outputs.length; i++)
if (tx1.outputs[i].value !== tx2.outputs[i].value || Crypto.util.bytesToHex(tx1.outputs[i].script) !== Crypto.util.bytesToHex(tx2.outputs[i].script))
return false;
return true;
}
floBlockchainAPI.transactionID = function (tx) {
tx = deserializeTx(tx);
let clone = bitjs.clone(tx);
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);
}
const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
fetch_api(`api/tx/${txid}`)
.then(result => resolve(result.vout[i]))
.catch(error => reject(error))
});
function getOutputAddress(outscript) {
var bytes, version;
switch (outscript[0]) {
case 118: //legacy
bytes = outscript.slice(3, outscript.length - 2);
version = bitjs.pub;
break
case 169: //multisig
bytes = outscript.slice(2, outscript.length - 1);
version = bitjs.multisig;
break;
default: return; //unknown
}
bytes.unshift(version);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, { asBytes: true }), { asBytes: true });
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
}
floBlockchainAPI.parseTransaction = function (tx) {
return new Promise((resolve, reject) => {
tx = deserializeTx(tx);
let result = {};
let promises = [];
//Parse Inputs
for (let i = 0; i < tx.inputs.length; i++)
promises.push(getTxOutput(tx.inputs[i].outpoint.hash, tx.inputs[i].outpoint.index));
Promise.all(promises).then(inputs => {
result.inputs = inputs.map(inp => Object({
address: inp.scriptPubKey.addresses[0],
value: parseFloat(inp.value)
}));
let signed = checkSigned(tx, false);
result.inputs.forEach((inp, i) => inp.signed = signed[i]);
//Parse Outputs
result.outputs = tx.outputs.map(out => Object({
address: getOutputAddress(out.script),
value: util.Sat_to_FLO(out.value)
}))
//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));
result.floData = tx.floData;
resolve(result);
}).catch(error => reject(error))
})
}
//Broadcast signed Tx in blockchain using API //Broadcast signed Tx in blockchain using API
const broadcastTx = floBlockchainAPI.broadcastTx = function (signedTxHash) { const broadcastTx = floBlockchainAPI.broadcastTx = function (signedTxHash) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //floCrypto v2.3.3e (function (EXPORTS) { //floCrypto v2.3.5a
/* FLO Crypto Operators */ /* FLO Crypto Operators */
'use strict'; 'use strict';
const floCrypto = EXPORTS; const floCrypto = EXPORTS;
@ -222,7 +222,7 @@
key.setCompressed(true); key.setCompressed(true);
if (isfloID && pubKey_floID == key.getBitcoinAddress()) if (isfloID && pubKey_floID == key.getBitcoinAddress())
return true; return true;
else if (!isfloID && pubKey_floID == key.getPubKeyHex()) else if (!isfloID && pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase())
return true; return true;
else else
return false; return false;
@ -231,12 +231,36 @@
} }
} }
floCrypto.getMultisigAddress = function (publicKeyList, requiredSignatures) {
if (!Array.isArray(publicKeyList) || !publicKeyList.length)
return null;
if (!Number.isInteger(requiredSignatures) || requiredSignatures < 1 || requiredSignatures > publicKeyList.length)
return null;
try {
var multisig = bitjs.pubkeys2multisig(publicKeyList, requiredSignatures);
return multisig;
} catch {
return null;
}
}
floCrypto.decodeRedeemScript = function (redeemScript) {
try {
var decoded = bitjs.transaction().decodeRedeemScript(redeemScript);
return decoded;
} catch {
return null;
}
}
//Check if the given flo-id is valid or not //Check if the given flo-id is valid or not
floCrypto.validateFloID = function (floID) { floCrypto.validateFloID = function (floID, regularOnly = false) {
if (!floID) if (!floID)
return false; return false;
try { try {
let addr = new Bitcoin.Address(floID); let addr = new Bitcoin.Address(floID);
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
return false;
return true; return true;
} catch { } catch {
return false; return false;
@ -266,13 +290,15 @@
return false; return false;
} }
//Check the public-key for the address (any blockchain) //Check the public-key (or redeem-script) for the address (any blockchain)
floCrypto.verifyPubKey = function (pubKeyHex, address) { floCrypto.verifyPubKey = function (pubKeyHex, address) {
let raw = decodeAddress(address), let raw = decodeAddress(address);
pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { if (!raw)
asBytes: true return;
}))); let pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })));
return raw ? pub_hash === raw.hex : false; if (typeof raw.bech_version !== 'undefined' && raw.bytes.length == 32) //bech32-multisig
raw.hex = Crypto.util.bytesToHex(ripemd160(raw.bytes, { asBytes: true }));
return pub_hash === raw.hex;
} }
//Convert the given address (any blockchain) to equivalent floID //Convert the given address (any blockchain) to equivalent floID
@ -282,7 +308,7 @@
let raw = decodeAddress(address); let raw = decodeAddress(address);
if (!raw) if (!raw)
return; return;
else if (options) { else if (options) { //if (optional) version check is passed
if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version))) if (typeof raw.version !== 'undefined' && (!options.std || !options.std.includes(raw.version)))
return; return;
if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version))) if (typeof raw.bech_version !== 'undefined' && (!options.bech || !options.bech.includes(raw.bech_version)))
@ -297,6 +323,35 @@
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4))); return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
} }
//Convert the given multisig address (any blockchain) to equivalent multisig floID
floCrypto.toMultisigFloID = function (address, options = null) {
if (!address)
return;
let raw = decodeAddress(address);
if (!raw)
return;
else if (options) { //if (optional) version check is passed
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;
}
if (typeof raw.bech_version !== 'undefined') {
if (raw.bytes.length != 32) return; //multisig bech address have 32 bytes
//multisig-bech:hash=SHA256 whereas multisig:hash=r160(SHA265), thus ripemd160 the bytes from multisig-bech
raw.bytes = ripemd160(raw.bytes, {
asBytes: true
});
}
raw.bytes.unshift(bitjs.multisig);
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
asBytes: true
}), {
asBytes: true
});
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
}
//Checks if the given addresses (any blockchain) are same (w.r.t keys) //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) if (!addr1 || !addr2)
@ -305,8 +360,13 @@
raw2 = decodeAddress(addr2); raw2 = decodeAddress(addr2);
if (!raw1 || !raw2) if (!raw1 || !raw2)
return false; return false;
else else {
if (typeof raw1.bech_version !== 'undefined' && raw1.bytes.length == 32) //bech32-multisig
raw1.hex = Crypto.util.bytesToHex(ripemd160(raw1.bytes, { asBytes: true }));
if (typeof raw2.bech_version !== 'undefined' && raw2.bytes.length == 32) //bech32-multisig
raw2.hex = Crypto.util.bytesToHex(ripemd160(raw2.bytes, { asBytes: true }));
return raw1.hex === raw2.hex; return raw1.hex === raw2.hex;
}
} }
const decodeAddress = floCrypto.decodeAddr = function (address) { const decodeAddress = floCrypto.decodeAddr = function (address) {
@ -326,7 +386,7 @@
hex: Crypto.util.bytesToHex(bytes), hex: Crypto.util.bytesToHex(bytes),
bytes bytes
} }
} else if (address.length == 42) { //bech encoding } else if (address.length == 42 || address.length == 62) { //bech encoding
let decode = coinjs.bech32_decode(address); let decode = coinjs.bech32_decode(address);
if (decode) { if (decode) {
let bytes = decode.data; let bytes = decode.data;

View File

@ -1,4 +1,4 @@
(function (GLOBAL) { //lib v1.3.2 (function (GLOBAL) { //lib v1.4.2b
'use strict'; 'use strict';
/* Utility Libraries required for Standard operations /* Utility Libraries required for Standard operations
* All credits for these codes belong to their respective creators, moderators and owners. * All credits for these codes belong to their respective creators, moderators and owners.
@ -4349,20 +4349,18 @@
var bitjs = GLOBAL.bitjs = function () { }; var bitjs = GLOBAL.bitjs = function () { };
function ascii_to_hexa(str) {
var arr1 = [];
for (var n = 0, l = str.length; n < l; n++) {
var hex = Number(str.charCodeAt(n)).toString(16);
arr1.push(hex);
}
return arr1.join('');
}
/* public vars */ /* public vars */
bitjs.pub = 0x23; // flochange - changed the prefix to FLO Mainnet PublicKey Prefix 0x23 bitjs.pub = 0x23; // flochange - changed the prefix to FLO Mainnet PublicKey Prefix 0x23
bitjs.priv = 0xa3; //flochange - changed the prefix to FLO Mainnet Private key prefix 0xa3 bitjs.priv = 0xa3; //flochange - changed the prefix to FLO Mainnet Private key prefix 0xa3
bitjs.multisig = 0x5e; //flochange - prefix for FLO Mainnet Multisig 0x5e
bitjs.compressed = false; bitjs.compressed = false;
if (GLOBAL.cryptocoin == 'FLO_TEST') {
bitjs.pub = 0x73; // flochange - changed the prefix to FLO TestNet PublicKey Prefix 0x73
bitjs.priv = 0xa3; //flochange - changed the prefix to FLO TestNet Private key prefix 0xa3
bitjs.multisig = 0xc6; //flochange - prefix for FLO TestNet Multisig 0xc6
}
/* provide a privkey and return an WIF */ /* provide a privkey and return an WIF */
bitjs.privkey2wif = function (h) { bitjs.privkey2wif = function (h) {
var r = Crypto.util.hexToBytes(h); var r = Crypto.util.hexToBytes(h);
@ -4461,7 +4459,46 @@
return B58.encode(r.concat(checksum)); return B58.encode(r.concat(checksum));
} }
bitjs.transaction = function () { /* generate a multisig address from pubkeys and required signatures */
bitjs.pubkeys2multisig = function (pubkeys, required) {
var s = [];
s.push(80 + required); //OP_1
for (var i = 0; i < pubkeys.length; ++i) {
let bytes = Crypto.util.hexToBytes(pubkeys[i]);
s.push(bytes.length);
s = s.concat(bytes);
}
s.push(80 + pubkeys.length); //OP_1
s.push(174); //OP_CHECKMULTISIG
if (s.length > 520) { // too large
throw Error(`redeemScript size(=${s.length}) too large`)
}
var x = ripemd160(Crypto.SHA256(s, {
asBytes: true
}), {
asBytes: true
});
x.unshift(bitjs.multisig);
var r = x;
r = Crypto.SHA256(Crypto.SHA256(r, {
asBytes: true
}), {
asBytes: true
});
var checksum = r.slice(0, 4);
var redeemScript = Crypto.util.bytesToHex(s);
var address = B58.encode(x.concat(checksum));
return {
'address': address,
'redeemScript': redeemScript,
'size': s.length
};
}
bitjs.transaction = function (tx_data = undefined) {
var btrx = {}; var btrx = {};
btrx.version = 2; //flochange look at this version btrx.version = 2; //flochange look at this version
btrx.inputs = []; btrx.inputs = [];
@ -4476,7 +4513,6 @@
'hash': txid, 'hash': txid,
'index': index 'index': index
}; };
//o.script = []; Signature and Public Key should be added after singning
o.script = Crypto.util.hexToBytes(scriptPubKey); //push previous output pubkey script o.script = Crypto.util.hexToBytes(scriptPubKey); //push previous output pubkey script
o.sequence = sequence || ((btrx.locktime == 0) ? 4294967295 : 0); o.sequence = sequence || ((btrx.locktime == 0) ? 4294967295 : 0);
return this.inputs.push(o); return this.inputs.push(o);
@ -4485,26 +4521,41 @@
btrx.addoutput = function (address, value) { btrx.addoutput = function (address, value) {
var o = {}; var o = {};
var buf = []; var buf = [];
var addrDecoded = btrx.addressDecode(address); var addr = this.addressDecode(address);
o.value = new BigInteger('' + Math.round((value * 1) * 1e8), 10); o.value = new BigInteger('' + Math.round((value * 1) * 1e8), 10);
buf.push(118); //OP_DUP
buf.push(169); //OP_HASH160 if (addr.version === bitjs.pub) { // regular address
buf.push(addrDecoded.length); buf.push(118); //OP_DUP
buf = buf.concat(addrDecoded); // address in bytes buf.push(169); //OP_HASH160
buf.push(136); //OP_EQUALVERIFY buf = this.writeBytesToScriptBuffer(buf, addr.bytes);// address in bytes
buf.push(172); // OP_CHECKSIG buf.push(136); //OP_EQUALVERIFY
buf.push(172); //OP_CHECKSIG
} else if (addr.version === bitjs.multisig) { // multisig address
buf.push(169); //OP_HASH160
buf = this.writeBytesToScriptBuffer(buf, addr.bytes);// address in bytes
buf.push(135); //OP_EQUAL
}
o.script = buf; o.script = buf;
return this.outputs.push(o); return this.outputs.push(o);
} }
// flochange - Added fn to assign flodata to tx
btrx.addflodata = function (data) {
//checks for valid flo-data string
if (typeof data !== "string")
throw Error("floData should be String");
if (data.length > 1040)
throw Error("floData Character Limit Exceeded");
if (bitjs.strToBytes(data).some(c => c < 32 || c > 127))
throw Error("floData contains Invalid characters (only ASCII characters allowed");
btrx.addflodata = function (txcomments) { // flochange - this whole function needs to be done this.floData = data;
this.floData = txcomments; return this.floData;
return this.floData; //flochange .. returning the txcomments -- check if the function return will assign
} }
// Only standard addresses // Only standard addresses (standard multisig supported)
btrx.addressDecode = function (address) { btrx.addressDecode = function (address) {
var bytes = B58.decode(address); var bytes = B58.decode(address);
var front = bytes.slice(0, bytes.length - 4); var front = bytes.slice(0, bytes.length - 4);
@ -4515,7 +4566,10 @@
asBytes: true asBytes: true
}).slice(0, 4); }).slice(0, 4);
if (checksum + "" == back + "") { if (checksum + "" == back + "") {
return front.slice(1); return {
version: front[0],
bytes: front.slice(1)
};
} }
} }
@ -4740,6 +4794,83 @@
return KBigInt; return KBigInt;
}; };
btrx.writeBytesToScriptBuffer = function (buf, bytes) {
if (bytes.length < 76) { //OP_PUSHDATA1
buf.push(bytes.length);
} else if (bytes.length <= 0xff) {
buf.push(76); //OP_PUSHDATA1
buf.push(bytes.length);
} else if (bytes.length <= 0xffff) {
buf.push(77); //OP_PUSHDATA2
buf.push(bytes.length & 0xff);
buf.push((bytes.length >>> 8) & 0xff);
} else {
buf.push(78); //OP_PUSHDATA4
buf.push(bytes.length & 0xff);
buf.push((bytes.length >>> 8) & 0xff);
buf.push((bytes.length >>> 16) & 0xff);
buf.push((bytes.length >>> 24) & 0xff);
}
buf = buf.concat(bytes);
return buf;
}
btrx.parseScript = function (script) {
var chunks = [];
var i = 0;
function readChunk(n) {
chunks.push(script.slice(i, i + n));
i += n;
};
while (i < script.length) {
var opcode = script[i++];
if (opcode >= 0xF0) {
opcode = (opcode << 8) | script[i++];
}
var len;
if (opcode > 0 && opcode < 76) { //OP_PUSHDATA1
readChunk(opcode);
} else if (opcode == 76) { //OP_PUSHDATA1
len = script[i++];
readChunk(len);
} else if (opcode == 77) { //OP_PUSHDATA2
len = (script[i++] << 8) | script[i++];
readChunk(len);
} else if (opcode == 78) { //OP_PUSHDATA4
len = (script[i++] << 24) | (script[i++] << 16) | (script[i++] << 8) | script[i++];
readChunk(len);
} else {
chunks.push(opcode);
}
if (i < 0x00) {
break;
}
}
return chunks;
}
btrx.decodeRedeemScript = function (rs) {
if (typeof rs == "string")
rs = Crypto.util.hexToBytes(rs);
var script = this.parseScript(rs);
if (!(script[0] > 80 && script[script.length - 2] > 80 && script[script.length - 1] == 174)) //OP_CHECKMULTISIG
throw "Invalid RedeemScript";
var r = {};
r.required = script[0] - 80;
r.pubkeys = [];
for (var i = 1; i < script.length - 2; i++)
r.pubkeys.push(Crypto.util.bytesToHex(script[i]));
r.address = bitjs.pubkeys2multisig(r.pubkeys, r.required).address;
r.redeemscript = Crypto.util.bytesToHex(rs);
return r;
}
/* sign a "standard" input */ /* sign a "standard" input */
btrx.signinput = function (index, wif, sigHashType) { btrx.signinput = function (index, wif, sigHashType) {
var key = bitjs.wif2pubkey(wif); var key = bitjs.wif2pubkey(wif);
@ -4747,8 +4878,7 @@
var signature = this.transactionSig(index, wif, shType); var signature = this.transactionSig(index, wif, shType);
var buf = []; var buf = [];
var sigBytes = Crypto.util.hexToBytes(signature); var sigBytes = Crypto.util.hexToBytes(signature);
buf.push(sigBytes.length); buf = this.writeBytesToScriptBuffer(buf, sigBytes);
buf = buf.concat(sigBytes);
var pubKeyBytes = Crypto.util.hexToBytes(key['pubkey']); var pubKeyBytes = Crypto.util.hexToBytes(key['pubkey']);
buf.push(pubKeyBytes.length); buf.push(pubKeyBytes.length);
buf = buf.concat(pubKeyBytes); buf = buf.concat(pubKeyBytes);
@ -4756,15 +4886,98 @@
return true; return true;
} }
/* sign a multisig input */
btrx.signmultisig = function (index, wif, sigHashType) {
var script = Array.from(this.inputs[index].script);
var redeemScript, sigsList = [];
if (script[0] == 0) { //script with signatures
script = this.parseScript(script);
for (var i = 0; i < script.length; i++) {
if (Array.isArray(script[i])) {
if (script[i][0] == 48) //0x30 DERSequence
sigsList.push(script[i]);
else if (script[i][0] >= 80 && script[i][script[i].length - 1] == 174) //OP_CHECKMULTISIG
redeemScript = script[i];
}
}
} else { //script = redeemscript
redeemScript = script;
}
var pubkeyList = this.decodeRedeemScript(redeemScript).pubkeys;
var pubkey = bitjs.wif2pubkey(wif)['pubkey'];
if (!pubkeyList.includes(pubkey)) //wif not a part of this multisig
return false;
pubkeyList = pubkeyList.map(pub => Crypto.util.hexToBytes(bitjs.pubkeydecompress(pub))); //decompress pubkeys
var shType = sigHashType || 1;
this.inputs[index].script = redeemScript; //script to be signed is redeemscript
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType));
sigsList.push(signature);
var buf = [];
buf.push(0);
//verify signatures and order them (also remove duplicate sigs)
for (let x in pubkeyList) {
for (let y in sigsList) {
var sighash = Crypto.util.hexToBytes(this.transactionHash(index, sigsList[y].slice(-1)[0] * 1));
if (bitjs.verifySignature(sighash, sigsList[y], pubkeyList[x])) {
buf = this.writeBytesToScriptBuffer(buf, sigsList[y]);
break; //ensures duplicate sigs from same pubkey are not added
}
}
}
//append redeemscript
buf = this.writeBytesToScriptBuffer(buf, redeemScript);
this.inputs[index].script = buf;
return true;
}
/* sign inputs */ /* sign inputs */
btrx.sign = function (wif, sigHashType) { btrx.sign = function (wif, sigHashType) {
var shType = sigHashType || 1; var shType = sigHashType || 1;
for (var i = 0; i < this.inputs.length; i++) { for (var i = 0; i < this.inputs.length; i++) {
this.signinput(i, wif, shType);
var decodedScript = this.scriptDecode(i);
if (decodedScript.type == "scriptpubkey" && decodedScript.signed == false) { //regular
var addr = bitjs.wif2address(wif)["address"];;
if (decodedScript.pubhash == Crypto.util.bytesToHex(this.addressDecode(addr).bytes)) //input belongs to wif
this.signinput(i, wif, shType);
} else if (decodedScript.type == "multisig") { //multisig
this.signmultisig(i, wif, shType);
}
} }
return this.serialize(); return this.serialize();
} }
// function to find type of the script in input
btrx.scriptDecode = function (index) {
var script = this.parseScript(this.inputs[index].script);
if (script.length == 5 && script[script.length - 1] == 172) {
//OP_DUP OP_HASH160 [address bytes] OP_EQUALVERIFY OP_CHECKSIG
// regular scriptPubkey (not signed)
return { type: 'scriptpubkey', signed: false, pubhash: Crypto.util.bytesToHex(script[2]) };
} else if (script.length == 2 && script[0][0] == 48) {
//[signature] [pubkey]
//(probably) regular signed
return { type: 'scriptpubkey', signed: true };
} else if (script[0] == 0 && script[script.length - 1][script[script.length - 1].length - 1] == 174) {
//0 [signatues] [redeemscript OP_CHECKMULTISIG]
// multisig with signature
return { type: 'multisig', rs: script[script.length - 1] };
} else if (script[0] >= 80 && script[script.length - 1] == 174) {
//redeemscript: 80+ [pubkeys] OP_CHECKMULTISIG
// multisig without signature
return { type: 'multisig', rs: Array.from(this.inputs[index].script) };
}
}
/* serialize a transaction */ /* serialize a transaction */
btrx.serialize = function () { btrx.serialize = function () {
@ -4793,28 +5006,85 @@
} }
buffer = buffer.concat(bitjs.numToBytes(parseInt(this.locktime), 4)); buffer = buffer.concat(bitjs.numToBytes(parseInt(this.locktime), 4));
var flohex = ascii_to_hexa(this.floData);
var floDataCount = this.floData.length;
var floDataCountString;
//flochange -- creating unique data character count logic for floData. This string is prefixed before actual floData string in Raw Transaction
if (floDataCount < 16) {
floDataCountString = floDataCount.toString(16);
floDataCountString = "0" + floDataCountString;
} else if (floDataCount < 253) {
floDataCountString = floDataCount.toString(16);
} else if (floDataCount <= 1040) {
let floDataCountAdjusted = (floDataCount - 253) + parseInt("0xfd00fd");
let floDataCountStringAdjusted = floDataCountAdjusted.toString(16);
floDataCountString = floDataCountStringAdjusted.substr(0, 2) + floDataCountStringAdjusted.substr(4, 2) + floDataCountStringAdjusted.substr(2, 2);
} else {
floDataCountString = "Character Limit Exceeded";
}
//flochange -- append floData field
buffer = buffer.concat(bitjs.numToVarInt(this.floData.length));
buffer = buffer.concat(bitjs.strToBytes(this.floData))
return Crypto.util.bytesToHex(buffer) + floDataCountString + flohex; // flochange -- Addition of floDataCountString and floData in serialization return Crypto.util.bytesToHex(buffer);
} }
/* deserialize a transaction */
function deserialize(buffer) {
if (typeof buffer == "string") {
buffer = Crypto.util.hexToBytes(buffer)
}
var pos = 0;
var readAsInt = function (bytes) {
if (bytes == 0) return 0;
pos++;
return buffer[pos - 1] + readAsInt(bytes - 1) * 256;
}
var readVarInt = function () {
pos++;
if (buffer[pos - 1] < 253) {
return buffer[pos - 1];
}
return readAsInt(buffer[pos - 1] - 251);
}
var readBytes = function (bytes) {
pos += bytes;
return buffer.slice(pos - bytes, pos);
}
var readVarString = function () {
var size = readVarInt();
return readBytes(size);
}
var bytesToStr = function (bytes) {
return bytes.map(b => String.fromCharCode(b)).join('');
}
const self = btrx;
self.version = readAsInt(4);
var ins = readVarInt();
for (var i = 0; i < ins; i++) {
self.inputs.push({
outpoint: {
hash: Crypto.util.bytesToHex(readBytes(32).reverse()),
index: readAsInt(4)
},
script: readVarString(),
sequence: readAsInt(4)
});
}
var outs = readVarInt();
for (var i = 0; i < outs; i++) {
self.outputs.push({
value: bitjs.bytesToNum(readBytes(8)),
script: readVarString()
});
}
self.lock_time = readAsInt(4);
//flochange - floData field
self.floData = bytesToStr(readVarString());
return self;
}
//deserialize the data if passed
if (tx_data)
deserialize(tx_data);
return btrx; return btrx;
@ -4856,6 +5126,36 @@
else return bytes[0] + 256 * bitjs.bytesToNum(bytes.slice(1)); else return bytes[0] + 256 * bitjs.bytesToNum(bytes.slice(1));
} }
//flochange - adding fn to convert string (for flodata) to byte
bitjs.strToBytes = function (str) {
return str.split('').map(c => c.charCodeAt(0));
}
/* decompress an compressed public key */
bitjs.pubkeydecompress = function (pubkey) {
if ((typeof (pubkey) == 'string') && pubkey.match(/^[a-f0-9]+$/i)) {
var curve = EllipticCurve.getSECCurveByName("secp256k1");
try {
var pt = curve.curve.decodePointHex(pubkey);
var x = pt.getX().toBigInteger();
var y = pt.getY().toBigInteger();
var publicKeyBytes = EllipticCurve.integerToBytes(x, 32);
publicKeyBytes = publicKeyBytes.concat(EllipticCurve.integerToBytes(y, 32));
publicKeyBytes.unshift(0x04);
return Crypto.util.bytesToHex(publicKeyBytes);
} catch (e) {
// console.log(e);
return false;
}
}
return false;
}
bitjs.verifySignature = function (hash, sig, pubkey) {
return Bitcoin.ECDSA.verify(hash, sig, pubkey);
}
/* clone an object */ /* clone an object */
bitjs.clone = function (obj) { bitjs.clone = function (obj) {
if (obj == null || typeof (obj) != 'object') return obj; if (obj == null || typeof (obj) != 'object') return obj;
@ -5020,17 +5320,25 @@
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/address.js //https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/address.js
Bitcoin.Address = function (bytes) { Bitcoin.Address = function (bytes) {
if (GLOBAL.cryptocoin == "FLO")
this.version = 0x23; // FLO mainnet public address
else if (GLOBAL.cryptocoin == "FLO_TEST")
this.version = 0x73; // FLO testnet public address
if ("string" == typeof bytes) { if ("string" == typeof bytes) {
bytes = Bitcoin.Address.decodeString(bytes, this.version); var d = Bitcoin.Address.decodeString(bytes);
bytes = d.hash;
if (d.version == Bitcoin.Address.standardVersion || d.version == Bitcoin.Address.multisigVersion)
this.version = d.version;
else throw "Version (prefix) " + d.version + " not supported!";
} else {
this.version = Bitcoin.Address.standardVersion;
} }
this.hash = bytes; this.hash = bytes;
}; };
Bitcoin.Address.networkVersion = 0x23; // (FLO mainnet 0x23, 35D), (Bitcoin Mainnet, 0x00, 0D) // *this has no effect * Bitcoin.Address.standardVersion = 0x23; // (FLO mainnet 0x23, 35D), (Bitcoin Mainnet, 0x00, 0D)
Bitcoin.Address.multisigVersion = 0x5e; // (FLO multisig 0x5e, 94D)
if (GLOBAL.cryptocoin == "FLO_TEST") {
Bitcoin.Address.standardVersion = 0x73; // (FLO testnet 0x73, 115D), (Bitcoin Mainnet, 0x00, 0D)
Bitcoin.Address.multisigVersion = 0xc6; // (FLO testnet multisig 0xc6, 198D)
}
/** /**
* Serialize this object as a standard Bitcoin address. * Serialize this object as a standard Bitcoin address.
@ -5059,7 +5367,7 @@
/** /**
* Parse a Bitcoin address contained in a string. * Parse a Bitcoin address contained in a string.
*/ */
Bitcoin.Address.decodeString = function (string, version) { Bitcoin.Address.decodeString = function (string) {
var bytes = Bitcoin.Base58.decode(string); var bytes = Bitcoin.Base58.decode(string);
var hash = bytes.slice(0, 21); var hash = bytes.slice(0, 21);
var checksum = Crypto.SHA256(Crypto.SHA256(hash, { var checksum = Crypto.SHA256(Crypto.SHA256(hash, {
@ -5075,11 +5383,12 @@
throw "Checksum validation failed!"; throw "Checksum validation failed!";
} }
if (version != hash.shift()) { /*if (version != hash.shift()) {
throw "Version " + hash.shift() + " not supported!"; throw "Version " + hash.shift() + " not supported!";
} }*/
return hash; var version = hash.shift();
return { version, hash };
}; };
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/e90780d3d3b8fc0d027d2bcb38b80479902f223e/src/ecdsa.js //https://raw.github.com/bitcoinjs/bitcoinjs-lib/e90780d3d3b8fc0d027d2bcb38b80479902f223e/src/ecdsa.js
Bitcoin.ECDSA = (function () { Bitcoin.ECDSA = (function () {
@ -6488,6 +6797,7 @@
return { return {
'address': address, 'address': address,
'redeemScript': r.redeemScript, 'redeemScript': r.redeemScript,
'scripthash': Crypto.util.bytesToHex(program),
'size': r.size 'size': r.size
}; };
} }
@ -6581,15 +6891,16 @@
}; };
} }
coinjs.multisigBech32Address = function (raw_redeemscript) { coinjs.multisigBech32Address = function (redeemscript) {
var program = Crypto.SHA256(Crypto.util.hexToBytes(raw_redeemscript), { var program = Crypto.SHA256(Crypto.util.hexToBytes(redeemscript), {
asBytes: true asBytes: true
}); });
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true))); var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
return { return {
'address': address, 'address': address,
'type': 'multisigBech32', 'type': 'multisigBech32',
'redeemscript': Crypto.util.bytesToHex(program) 'redeemScript': redeemscript,
'scripthash': Crypto.util.bytesToHex(program)
}; };
} }
@ -7587,7 +7898,7 @@
var n = u.getElementsByTagName("tx_output_n")[0].childNodes[0].nodeValue; var n = u.getElementsByTagName("tx_output_n")[0].childNodes[0].nodeValue;
var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue; var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
if (segwit) { //also for MULTISIG_BECH32 (p2wsh-multisig)(script = raw_redeemscript; for p2wsh-multisig) if (segwit) { //also for MULTISIG_BECH32 (p2wsh-multisig)(script = redeemscript; for p2wsh-multisig)
/* this is a small hack to include the value with the redeemscript to make the signing procedure smoother. /* this is a small hack to include the value with the redeemscript to make the signing procedure smoother.
It is not standard and removed during the signing procedure. */ It is not standard and removed during the signing procedure. */