This commit is contained in:
sairaj mote 2023-07-06 13:34:07 +05:30
commit 270524b702
20 changed files with 2129 additions and 416 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
*.tmp*

21
LICENCE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Sai Raj
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -13,7 +13,7 @@
<link
href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&family=Rubik:ital,wght@0,400;0,500;0,700;1,400;1,500;1,700&display=swap"
rel="stylesheet">
<script src="components.js" defer></script>
<script src="scripts/components.js" defer></script>
<script id="floGlobals">
/* Constants for FLO blockchain operations !!Make sure to add this at beginning!! */
const floGlobals = {
@ -22,18 +22,18 @@
application: "InternManage"
}
</script>
<script src="scripts/lib.min.js" defer></script>
<script src="scripts/floCrypto.min.js" defer></script>
<script src="scripts/floBlockchainAPI.min.js" defer></script>
<script src="scripts/compactIDB.min.js" defer></script>
<script src="scripts/floCloudAPI.min.js" defer></script>
<script src="scripts/floDapps.min.js" defer></script>
<script src="scripts/btcOperator.min.js" defer></script>
<script src="scripts/lib.js" defer></script>
<script src="scripts/floCrypto.js" defer></script>
<script src="scripts/floBlockchainAPI.js" defer></script>
<script src="scripts/compactIDB.js" defer></script>
<script src="scripts/floCloudAPI.js" defer></script>
<script src="scripts/floDapps.js" defer></script>
<script src="scripts/btcOperator.js" defer></script>
<script src="https://unpkg.com/uhtml@3.0.1/es.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/2.4.0/purify.min.js"
integrity="sha512-/hVAZO5POxCKdZMSLefw30xEVwjm94PAV9ynjskGbIpBvHO9EBplEcdUlBdCKutpZsF+La8Ag4gNrG0gAOn3Ig=="
crossorigin="anonymous" referrerpolicy="no-referrer" defer></script>
<script src="scripts/ribc.min.js" defer></script>
<script src="scripts/ribc.js" defer></script>
<script id="onLoadStartUp">
function onLoadStartUp() {
routeTo('loading')
@ -3934,4 +3934,4 @@
</script>
</body>
</htm>
</htm>

View File

@ -1,29 +1,43 @@
(function (EXPORTS) { //btcOperator v1.0.12
(function (EXPORTS) { //btcOperator v1.1.3b
/* BTC Crypto and API Operator */
const btcOperator = EXPORTS;
//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 DUST_AMT = 546,
MIN_FEE_UPDATE = 219;
const fetch_api = btcOperator.fetch = function (api, json_res = true) {
return new Promise((resolve, reject) => {
console.debug(URL + api);
fetch(URL + api).then(response => {
response.json()
.then(result => result.status === "success" ? resolve(result) : reject(result))
.catch(error => reject(error))
if (response.ok) {
(json_res ? response.json() : response.text())
.then(result => resolve(result))
.catch(error => reject(error))
} else {
response.json()
.then(result => reject(result))
.catch(error => reject(error))
}
}).catch(error => reject(error))
})
};
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() {
return new Promise((resolve, reject) => {
fetch('https://api.blockchain.info/mempool/fees').then(response => {
if (response.ok)
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));
else
reject(response);
@ -102,64 +116,101 @@
if (!addr)
return undefined;
let type = coinjs.addressDecode(addr).type;
if (["standard", "multisig", "bech32"].includes(type))
if (["standard", "multisig", "bech32", "multisigBech32"].includes(type))
return type;
else
return false;
}
btcOperator.multiSigAddress = function (pubKeys, minRequired) {
btcOperator.multiSigAddress = function (pubKeys, minRequired, bech32 = true) {
if (!Array.isArray(pubKeys))
throw "pubKeys must be an array of public keys";
else if (pubKeys.length < minRequired)
throw "minimum required should be less than the number of pubKeys";
return coinjs.pubkeys2MultisigAddress(pubKeys, minRequired);
if (bech32)
return coinjs.pubkeys2MultisigAddressBech32(pubKeys, minRequired);
else
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)
btcOperator.convert = {};
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))
return null;
else
return encodeLegacy(keyHex, target_version);
return util.encodeLegacy(keyHex, target_version);
}
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)
return null;
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) {
let rawHex = decodeLegacy(source_addr).hex;
let rawHex = util.decodeLegacy(source_addr).hex;
if (!rawHex)
return null;
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) {
let rawHex = decodeBech32(source_addr).hex;
let rawHex = util.decodeBech32(source_addr).hex;
if (!rawHex)
return null;
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) {
let rawHex = decodeBech32(source_addr).hex;
let rawHex = util.decodeBech32(source_addr).hex;
if (!rawHex)
return null;
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 raw = decode.slice(0, decode.length - 4),
checksum = decode.slice(decode.length - 4);
@ -169,7 +220,7 @@
asBytes: true
});
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();
return {
version: version,
@ -177,7 +228,7 @@
}
}
function encodeLegacy(hex, version) {
util.encodeLegacy = function (hex, version) {
var bytes = Crypto.util.hexToBytes(hex);
bytes.unshift(version);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
@ -189,10 +240,10 @@
return coinjs.base58encode(bytes.concat(checksum));
}
function decodeBech32(source) {
util.decodeBech32 = function (source) {
let decode = coinjs.bech32_decode(source);
if (!decode)
return null;
return false;
var raw = decode.data;
let version = raw.shift();
raw = coinjs.bech32_convert(raw, 5, 8, false);
@ -203,7 +254,7 @@
}
}
function encodeBech32(hex, version, hrp) {
util.encodeBech32 = function (hex, version, hrp) {
var bytes = Crypto.util.hexToBytes(hex);
bytes = coinjs.bech32_convert(bytes, 8, 5, true);
bytes.unshift(version)
@ -213,8 +264,8 @@
//BTC blockchain APIs
btcOperator.getBalance = addr => new Promise((resolve, reject) => {
fetch_api(`get_address_balance/BTC/${addr}`)
.then(result => resolve(parseFloat(result.data.confirmed_balance)))
fetch_api(`q/addressbalance/${addr}`)
.then(result => resolve(util.Sat_to_BTC(result)))
.catch(error => reject(error))
});
@ -222,11 +273,13 @@
BASE_INPUT_SIZE = 41,
LEGACY_INPUT_SIZE = 107,
BECH32_INPUT_SIZE = 27,
BECH32_MULTISIG_INPUT_SIZE = 35,
SEGWIT_INPUT_SIZE = 59,
MULTISIG_INPUT_SIZE_ES = 351,
BASE_OUTPUT_SIZE = 9,
LEGACY_OUTPUT_SIZE = 25,
BECH32_OUTPUT_SIZE = 23,
BECH32_MULTISIG_OUTPUT_SIZE = 34,
SEGWIT_OUTPUT_SIZE = 23;
function _redeemScript(addr, key) {
@ -249,6 +302,8 @@
return BASE_INPUT_SIZE + LEGACY_INPUT_SIZE;
case "bech32":
return BASE_INPUT_SIZE + BECH32_INPUT_SIZE;
case "multisigBech32":
return BASE_INPUT_SIZE + BECH32_MULTISIG_INPUT_SIZE;
case "multisig":
switch (coinjs.script().decodeRedeemScript(rs).type) {
case "segwit__":
@ -269,6 +324,8 @@
return BASE_OUTPUT_SIZE + LEGACY_OUTPUT_SIZE;
case "bech32":
return BASE_OUTPUT_SIZE + BECH32_OUTPUT_SIZE;
case "multisigBech32":
return BASE_OUTPUT_SIZE + BECH32_MULTISIG_OUTPUT_SIZE;
case "multisig":
return BASE_OUTPUT_SIZE + SEGWIT_OUTPUT_SIZE;
default:
@ -299,7 +356,7 @@
parameters.privkeys[i] = coinjs.privkey2wif(key);
});
if (invalids.length)
throw "Invalid keys:" + invalids;
throw "Invalid private key for address:" + invalids;
}
//receiver-ids (and change-id)
if (!Array.isArray(parameters.receivers))
@ -307,8 +364,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;
@ -323,18 +380,37 @@
return parameters;
}
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 total_amount = parseFloat(amounts.reduce((t, a) => t + a, 0).toFixed(8));
const tx = coinjs.transaction();
let output_size = addOutputs(tx, receivers, amounts, change_addr);
addInputs(tx, senders, redeemScripts, total_amount, fee, output_size).then(result => {
if (result.change_amount > 0)
tx.outs[tx.outs.length - 1].value = parseInt(result.change_amount * SATOSHI_IN_BTC); //values are in satoshi
else
tx.outs.pop(); //remove the change output if no change_amount
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 && result.change_amount > result.fee) //add change amount if any (ignore dust change)
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
let fee_remaining = util.BTC_to_Sat(result.fee);
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");
}
//remove all output with value less than DUST amount
let filtered_outputs = [], dust_value = 0;
tx.outs.forEach(o => o.value >= DUST_AMT ? filtered_outputs.push(o) : dust_value += o.value);
tx.outs = filtered_outputs;
//update result values
result.fee += util.Sat_to_BTC(dust_value);
result.output_size = output_size;
result.output_amount = total_amount;
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);
@ -342,10 +418,10 @@
})
}
function addInputs(tx, senders, redeemScripts, total_amount, fee, output_size) {
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, total_amount + fee, false).then(result => {
addUTXOs(tx, senders, redeemScripts, fee_from_receiver ? total_amount : total_amount + fee, false).then(result => {
result.fee = fee;
resolve(result);
}).catch(error => reject(error))
@ -353,7 +429,10 @@
get_fee_rate().then(fee_rate => {
let net_fee = BASE_TX_SIZE * fee_rate;
net_fee += (output_size * fee_rate);
addUTXOs(tx, senders, redeemScripts, total_amount + net_fee, fee_rate).then(result => {
(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);
@ -381,30 +460,31 @@
return reject("Insufficient Balance");
let addr = senders[rec_args.n],
rs = redeemScripts[rec_args.n];
let addr_type = coinjs.addressDecode(addr).type;
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);
fetch_api(`unspent?active=${addr}`).then(result => {
let utxos = result.unspent_outputs;
//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;
var script;
if (!rs || !rs.length) //legacy script
script = utxos[i].script_hex;
else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi))) {
//redeemScript for segwit/bech32
script = utxos[i].script;
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)
let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(rs));
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);
} else //redeemScript for multisig
} else //redeemScript for multisig (segwit)
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
rec_args.input_size += size_per_input;
rec_args.input_amount += parseFloat(utxos[i].value);
required_amount -= parseFloat(utxos[i].value);
rec_args.input_amount += util.Sat_to_BTC(utxos[i].value);
required_amount -= util.Sat_to_BTC(utxos[i].value);
if (fee_rate) //automatic fee calculation (dynamic)
required_amount += size_per_input * fee_rate;
}
@ -416,14 +496,14 @@
})
}
function addOutputs(tx, receivers, amounts, change_addr) {
function addOutputs(tx, receivers, amounts, change_address) {
let size = 0;
for (let i in receivers) {
tx.addoutput(receivers[i], amounts[i]);
size += _sizePerOutput(receivers[i]);
}
tx.addoutput(change_addr, 0);
size += _sizePerOutput(change_addr);
tx.addoutput(change_address, 0);
size += _sizePerOutput(change_address);
return size;
}
@ -464,9 +544,109 @@
}
*/
btcOperator.sendTx = function (senders, privkeys, receivers, amounts, fee, change_addr = null) {
function tx_fetch_for_editing(tx) {
return new Promise((resolve, reject) => {
createSignedTx(senders, privkeys, receivers, amounts, fee, change_addr).then(result => {
if (typeof tx == 'string' && /^[0-9a-f]{64}$/i.test(tx)) { //tx is txid
getTx.hex(tx)
.then(txhex => resolve(deserializeTx(txhex)))
.catch(error => reject(error))
} else resolve(deserializeTx(tx));
})
}
btcOperator.editFee = function (tx_hex, new_fee, private_keys, change_only = true) {
return new Promise((resolve, reject) => {
if (!Array.isArray(private_keys))
private_keys = [private_keys];
tx_fetch_for_editing(tx_hex).then(tx => {
parseTransaction(tx).then(tx_parsed => {
if (tx_parsed.fee >= new_fee)
return reject("Fees can only be increased");
//editable addresses in output values (for fee increase)
var edit_output_address = new Set();
if (change_only === true) //allow only change values (ie, sender address) to be edited to inc fee
tx_parsed.inputs.forEach(inp => edit_output_address.add(inp.address));
else if (change_only === false) //allow all output values to be edited
tx_parsed.outputs.forEach(out => edit_output_address.add(out.address));
else if (typeof change_only == 'string') // allow only given receiver id output to be edited
edit_output_address.add(change_only);
else if (Array.isArray(change_only)) //allow only given set of receiver id outputs to be edited
change_only.forEach(id => edit_output_address.add(id));
//edit output values to increase fee
let inc_fee = util.BTC_to_Sat(new_fee - tx_parsed.fee);
if (inc_fee < MIN_FEE_UPDATE)
return reject(`Insufficient additional fee. Minimum increment: ${MIN_FEE_UPDATE}`);
for (let i = tx.outs.length - 1; i >= 0 && inc_fee > 0; i--) //reduce in reverse order
if (edit_output_address.has(tx_parsed.outputs[i].address)) {
let current_value = tx.outs[i].value;
if (current_value instanceof BigInteger) //convert BigInteger class to inv value
current_value = current_value.intValue();
//edit the value as required
if (current_value > inc_fee) {
tx.outs[i].value = current_value - inc_fee;
inc_fee = 0;
} else {
inc_fee -= current_value;
tx.outs[i].value = 0;
}
}
if (inc_fee > 0) {
let max_possible_fee = util.BTC_to_Sat(new_fee) - inc_fee; //in satoshi
return reject(`Insufficient output values to increase fee. Maximum fee possible: ${util.Sat_to_BTC(max_possible_fee)}`);
}
tx.outs = tx.outs.filter(o => o.value >= DUST_AMT); //remove all output with value less than DUST amount
//remove existing signatures and reset the scripts
let wif_keys = [];
for (let i in tx.ins) {
var addr = tx_parsed.inputs[i].address,
value = util.BTC_to_Sat(tx_parsed.inputs[i].value);
let addr_decode = coinjs.addressDecode(addr);
//find the correct key for addr
var privKey = private_keys.find(pk => verifyKey(addr, pk));
if (!privKey)
return reject(`Private key missing for ${addr}`);
//find redeemScript (if any)
const rs = _redeemScript(addr, privKey);
rs === false ? wif_keys.unshift(privKey) : wif_keys.push(privKey); //sorting private-keys (wif)
//reset the script for re-signing
var script;
if (!rs || !rs.length) {
//legacy script (derive from address)
let s = coinjs.script();
s.writeOp(118); //OP_DUP
s.writeOp(169); //OP_HASH160
s.writeBytes(addr_decode.bytes);
s.writeOp(136); //OP_EQUALVERIFY
s.writeOp(172); //OP_CHECKSIG
script = Crypto.util.bytesToHex(s.buffer);
} else if (((rs.match(/^00/) && rs.length == 44)) || (rs.length == 40 && rs.match(/^[a-f0-9]+$/gi)) || addr_decode.type === 'multisigBech32') {
//redeemScript for segwit/bech32 and multisig (bech32)
let s = coinjs.script();
s.writeBytes(Crypto.util.hexToBytes(rs));
s.writeOp(0);
s.writeBytes(coinjs.numToBytes(value.toFixed(0), 8));
script = Crypto.util.bytesToHex(s.buffer);
} else //redeemScript for multisig (segwit)
script = rs;
tx.ins[i].script = coinjs.script(script);
}
tx.witness = false; //remove all witness signatures
console.debug("Unsigned:", tx.serialize());
//re-sign the transaction
new Set(wif_keys).forEach(key => tx.sign(key, 1 /*sighashtype*/)); //Sign the tx using private key WIF
resolve(tx.serialize());
}).catch(error => reject(error))
}).catch(error => reject(error))
})
}
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))
@ -475,7 +655,7 @@
})
}
const createSignedTx = btcOperator.createSignedTx = function (senders, privkeys, receivers, amounts, fee = null, change_addr = null) {
const createSignedTx = btcOperator.createSignedTx = function (senders, privkeys, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
try {
({
@ -489,7 +669,7 @@
receivers,
amounts,
fee,
change_addr
change_address: options.change_address
}));
} catch (e) {
return reject(e)
@ -504,17 +684,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(result => {
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
new Set(wif_keys).forEach(key => tx.sign(key, 1 /*sighashtype*/)); //Sign the tx using private key WIF
console.debug("Signed:", tx.serialize());
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 {
({
@ -526,7 +706,7 @@
receivers,
amounts,
fee,
change_addr
change_address: options.change_address
}));
} catch (e) {
return reject(e)
@ -535,7 +715,7 @@
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(result => {
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);
@ -543,14 +723,17 @@
})
}
btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee = null) {
btcOperator.createMultiSigTx = function (sender, redeemScript, receivers, amounts, fee = null, options = {}) {
return new Promise((resolve, reject) => {
//validate tx parameters
if (validateAddress(sender) !== "multisig")
let addr_type = validateAddress(sender);
if (!(["multisig", "multisigBech32"].includes(addr_type)))
return reject("Invalid sender (multisig):" + sender);
else {
let script = coinjs.script();
let decode = script.decodeRedeemScript(redeemScript);
let decode = (addr_type == "multisig") ?
script.decodeRedeemScript(redeemScript) :
script.decodeRedeemScriptBech32(redeemScript);
if (!decode || decode.address !== sender)
return reject("Invalid redeem-script");
}
@ -561,13 +744,14 @@
} = 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(result => {
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);
@ -604,10 +788,10 @@
let n = [];
for (let i in tx.ins) {
var s = tx.extractScriptKey(i);
if (s['type'] !== 'multisig')
if (s['type'] !== 'multisig' && s['type'] !== 'multisig_bech32')
n.push(s.signed == 'true' || (tx.witness[i] && tx.witness[i].length == 2))
else {
var rs = coinjs.script().decodeRedeemScript(s.script);
var rs = coinjs.script().decodeRedeemScript(s.script); //will work for bech32 too, as only address is diff
let x = {
s: s['signatures'],
r: rs['signaturesRequired'],
@ -627,24 +811,27 @@
btcOperator.checkIfSameTx = function (tx1, tx2) {
tx1 = deserializeTx(tx1);
tx2 = deserializeTx(tx2);
//compare input and output length
if (tx1.ins.length !== tx2.ins.length || tx1.outs.length !== tx2.outs.length)
return false;
//compare inputs
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)
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))
return false;
return true;
}
const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
fetch_api(`get_tx_outputs/BTC/${txid}/${i}`)
.then(result => resolve(result.data.outputs))
fetch_api(`rawtx/${txid}`)
.then(result => resolve(result.out[i]))
.catch(error => reject(error))
});
btcOperator.parseTransaction = function (tx) {
const parseTransaction = btcOperator.parseTransaction = function (tx) {
return new Promise((resolve, reject) => {
tx = deserializeTx(tx);
let result = {};
@ -654,8 +841,8 @@
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)
address: inp.addr,
value: util.Sat_to_BTC(inp.value)
}));
let signed = checkSigned(tx, false);
result.inputs.forEach((inp, i) => inp.signed = signed[i]);
@ -663,18 +850,18 @@
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);
case 0: //bech32, multisig-bech32
address = util.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);
case 169: //segwit, multisig-segwit
address = util.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);
address = util.encodeLegacy(Crypto.util.bytesToHex(out.script.chunks[2]), coinjs.pub);
}
return {
address,
value: parseFloat(out.value / SATOSHI_IN_BTC)
value: util.Sat_to_BTC(out.value)
}
});
//Parse Totals
@ -695,22 +882,115 @@
return Crypto.util.bytesToHex(txid);
}
btcOperator.getTx = txid => new Promise((resolve, reject) => {
fetch_api(`tx/BTC/${txid}`)
.then(result => resolve(result.data))
const getLatestBlock = btcOperator.getLatestBlock = () => new Promise((resolve, reject) => {
fetch_api(`q/getblockcount`)
.then(result => resolve(result))
.catch(error => reject(error))
})
const getTx = 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) => {
fetch_api(`address/BTC/${addr}`)
.then(result => resolve(result.data))
getTx.hex = txid => new Promise((resolve, reject) => {
fetch_api(`rawtx/${txid}?format=hex`, false)
.then(result => resolve(result))
.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) => {
fetch_api(`get_block/BTC/${block}`)
.then(result => resolve(result.data))
.catch(error => reject(error))
fetch_api(`rawblock/${block}`).then(result => resolve({
height: result.height,
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 = {});

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //compactIDB v2.1.0
(function (EXPORTS) { //compactIDB v2.1.2
/* Compact IndexedDB operations */
'use strict';
const compactIDB = EXPORTS;
@ -226,10 +226,9 @@
compactIDB.searchData = function (obsName, options = {}, dbName = defaultDB) {
options.lowerKey = options.atKey || options.lowerKey || 0
options.upperKey = options.atKey || options.upperKey || false
options.patternEval = options.patternEval || ((k, v) => {
return true
})
options.patternEval = options.patternEval || ((k, v) => true);
options.limit = options.limit || false;
options.reverse = options.reverse || false;
options.lastOnly = options.lastOnly || false
return new Promise((resolve, reject) => {
openDB(dbName).then(db => {
@ -237,17 +236,16 @@
var filteredResult = {}
let curReq = obs.openCursor(
options.upperKey ? IDBKeyRange.bound(options.lowerKey, options.upperKey) : IDBKeyRange.lowerBound(options.lowerKey),
options.lastOnly ? "prev" : "next");
options.lastOnly || options.reverse ? "prev" : "next");
curReq.onsuccess = (evt) => {
var cursor = evt.target.result;
if (cursor) {
if (options.patternEval(cursor.primaryKey, cursor.value)) {
filteredResult[cursor.primaryKey] = cursor.value;
options.lastOnly ? resolve(filteredResult) : cursor.continue();
} else
cursor.continue();
if (!cursor || (options.limit && options.limit <= Object.keys(filteredResult).length))
return resolve(filteredResult); //reached end of key list or limit reached
else if (options.patternEval(cursor.primaryKey, cursor.value)) {
filteredResult[cursor.primaryKey] = cursor.value;
options.lastOnly ? resolve(filteredResult) : cursor.continue();
} else
resolve(filteredResult);
cursor.continue();
}
curReq.onerror = (evt) => reject(`Search unsuccessful [${evt.target.error.name}] ${evt.target.error.message}`);
db.close();

View File

@ -1 +0,0 @@
(function(e){"use strict";function r(e=a){return new Promise((r,t)=>{c(e).then(e=>{r(e.version),e.close()}).catch(e=>t(e))})}function t(e,t=null,n=null){return new Promise((a,s)=>{r(e).then(r=>{var c=o.open(e,r+1);c.onerror=(e=>s("Error in opening IndexedDB")),c.onupgradeneeded=(e=>{let r=e.target.result;if(t instanceof Object){if(Array.isArray(t)){let e={};t.forEach(r=>e[r]={}),t=e}for(let e in t){let n=r.createObjectStore(e,t[e].options||{});if(t[e].indexes instanceof Object)for(let r in t[e].indexes)n.createIndex(r,r,t[e].indexes||{})}}Array.isArray(n)&&n.forEach(e=>r.deleteObjectStore(e)),a("Database upgraded")}),c.onsuccess=(e=>e.target.result.close())}).catch(e=>s(e))})}const n=e;var a;const o=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB,s=(window.IDBTransaction||window.webkitIDBTransaction||window.msIDBTransaction,window.IDBKeyRange||window.webkitIDBKeyRange||window.msIDBKeyRange);if(!o)return void console.error("Your browser doesn't support a stable version of IndexedDB.");n.setDefaultDB=(e=>a=e),Object.defineProperty(n,"default",{get:()=>a,set:e=>a=e}),n.initDB=function(e,r={}){return new Promise((n,s)=>{if(!(r instanceof Object))return s("ObjectStores must be an object or array");a=a||e;var c=o.open(e);c.onerror=(e=>s("Error in opening IndexedDB")),c.onsuccess=(a=>{var o=a.target.result;let c=Object.values(o.objectStoreNames);var u={},l={},i=[];if(Array.isArray(r))r.forEach(e=>u[e]={});else u=r;let d=Object.keys(u);for(let e of d)c.includes(e)||(l[e]=u[e]);for(let e of c)d.includes(e)||i.push(e);Object.keys(l).length||i.length?t(e,l,i).then(e=>n(e)).catch(e=>s(e)):n("Initiated IndexedDB"),o.close()})})};const c=n.openDB=function(e=a){return new Promise((r,t)=>{var n=o.open(e);n.onerror=(e=>t("Error in opening IndexedDB")),n.onupgradeneeded=(r=>{r.target.result.close(),u(e).then(e=>null).catch(e=>null).finally(e=>t("Datebase not found"))}),n.onsuccess=(e=>r(e.target.result))})},u=n.deleteDB=function(e=a){return new Promise((r,t)=>{var n=o.deleteDatabase(e);n.onerror=(e=>t("Error deleting database!")),n.onsuccess=(e=>r("Database deleted successfully"))})};n.writeData=function(e,r,t=!1,n=a){return new Promise((a,o)=>{c(n).then(n=>{var s=n.transaction(e,"readwrite").objectStore(e);let c=t?s.put(r,t):s.put(r);c.onsuccess=(e=>a("Write data Successful")),c.onerror=(e=>o(`Write data unsuccessful [${e.target.error.name}] ${e.target.error.message}`)),n.close()}).catch(e=>o(e))})},n.addData=function(e,r,t=!1,n=a){return new Promise((a,o)=>{c(n).then(n=>{var s=n.transaction(e,"readwrite").objectStore(e);let c=t?s.add(r,t):s.add(r);c.onsuccess=(e=>a("Add data successful")),c.onerror=(e=>o(`Add data unsuccessful [${e.target.error.name}] ${e.target.error.message}`)),n.close()}).catch(e=>o(e))})},n.removeData=function(e,r,t=a){return new Promise((n,a)=>{c(t).then(t=>{var o=t.transaction(e,"readwrite").objectStore(e);let s=o.delete(r);s.onsuccess=(e=>n(`Removed Data ${r}`)),s.onerror=(e=>a(`Remove data unsuccessful [${e.target.error.name}] ${e.target.error.message}`)),t.close()}).catch(e=>a(e))})},n.clearData=function(e,r=a){return new Promise((t,n)=>{c(r).then(r=>{var a=r.transaction(e,"readwrite").objectStore(e);let o=a.clear();o.onsuccess=(e=>t("Clear data Successful")),o.onerror=(e=>n("Clear data Unsuccessful")),r.close()}).catch(e=>n(e))})},n.readData=function(e,r,t=a){return new Promise((n,a)=>{c(t).then(t=>{var o=t.transaction(e,"readonly").objectStore(e);let s=o.get(r);s.onsuccess=(e=>n(e.target.result)),s.onerror=(e=>a(`Read data unsuccessful [${e.target.error.name}] ${e.target.error.message}`)),t.close()}).catch(e=>a(e))})},n.readAllData=function(e,r=a){return new Promise((t,n)=>{c(r).then(r=>{var a=r.transaction(e,"readonly").objectStore(e),o={};let s=a.openCursor();s.onsuccess=(e=>{var r=e.target.result;r?(o[r.primaryKey]=r.value,r.continue()):t(o)}),s.onerror=(e=>n(`Read-All data unsuccessful [${e.target.error.name}] ${e.target.error.message}`)),r.close()}).catch(e=>n(e))})},n.searchData=function(e,r={},t=a){return r.lowerKey=r.atKey||r.lowerKey||0,r.upperKey=r.atKey||r.upperKey||!1,r.patternEval=r.patternEval||((e,r)=>!0),r.limit=r.limit||!1,r.lastOnly=r.lastOnly||!1,new Promise((n,a)=>{c(t).then(t=>{var o=t.transaction(e,"readonly").objectStore(e),c={};let u=o.openCursor(r.upperKey?s.bound(r.lowerKey,r.upperKey):s.lowerBound(r.lowerKey),r.lastOnly?"prev":"next");u.onsuccess=(e=>{var t=e.target.result;t?r.patternEval(t.primaryKey,t.value)?(c[t.primaryKey]=t.value,r.lastOnly?n(c):t.continue()):t.continue():n(c)}),u.onerror=(e=>a(`Search unsuccessful [${e.target.error.name}] ${e.target.error.message}`)),t.close()}).catch(e=>a(e))})}})(window.compactIDB={});

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //floCloudAPI v2.4.3
(function (EXPORTS) { //floCloudAPI v2.4.3a
/* FLO Cloud operations to send/request application data*/
'use strict';
const floCloudAPI = EXPORTS;
@ -8,6 +8,7 @@
SNStorageID: floGlobals.SNStorageID || "FNaN9McoBAEFUjkRmNQRYLmBF8SpS7Tgfk",
adminID: floGlobals.adminID,
application: floGlobals.application,
SNStorageName: "SuperNodeStorage",
callback: (d, e) => console.debug(d, e)
};
@ -58,6 +59,9 @@
SNStorageID: {
get: () => DEFAULT.SNStorageID
},
SNStorageName: {
get: () => DEFAULT.SNStorageName
},
adminID: {
get: () => DEFAULT.adminID
},

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //floCrypto v2.3.3d
(function (EXPORTS) { //floCrypto v2.3.6a
/* FLO Crypto Operators */
'use strict';
const floCrypto = EXPORTS;
@ -152,6 +152,19 @@
newID: {
get: () => generateNewID()
},
hashID: {
value: (str) => {
let bytes = ripemd160(Crypto.SHA256(str, { asBytes: true }), { asBytes: true });
bytes.unshift(bitjs.pub);
var hash = Crypto.SHA256(Crypto.SHA256(bytes, {
asBytes: true
}), {
asBytes: true
});
var checksum = hash.slice(0, 4);
return bitjs.Base58.encode(bytes.concat(checksum));
}
},
tmpID: {
get: () => {
let bytes = Crypto.util.randomBytes(20);
@ -222,7 +235,7 @@
key.setCompressed(true);
if (isfloID && pubKey_floID == key.getBitcoinAddress())
return true;
else if (!isfloID && pubKey_floID == key.getPubKeyHex())
else if (!isfloID && pubKey_floID.toUpperCase() == key.getPubKeyHex().toUpperCase())
return true;
else
return false;
@ -231,12 +244,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
floCrypto.validateFloID = function (floID) {
floCrypto.validateFloID = function (floID, regularOnly = false) {
if (!floID)
return false;
try {
let addr = new Bitcoin.Address(floID);
if (regularOnly && addr.version != Bitcoin.Address.standardVersion)
return false;
return true;
} catch {
return false;
@ -266,22 +303,30 @@
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) {
let raw = decodeAddress(address),
pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), {
asBytes: true
})));
return raw ? pub_hash === raw.hex : false;
let raw = decodeAddress(address);
if (!raw)
return;
let pub_hash = Crypto.util.bytesToHex(ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(pubKeyHex), { asBytes: true })));
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
floCrypto.toFloID = function (address) {
floCrypto.toFloID = 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;
}
raw.bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(Crypto.SHA256(raw.bytes, {
asBytes: true
@ -291,6 +336,50 @@
return bitjs.Base58.encode(raw.bytes.concat(hash.slice(0, 4)));
}
//Convert raw address bytes to floID
floCrypto.rawToFloID = function (raw_bytes) {
if (typeof raw_bytes === 'string')
raw_bytes = Crypto.util.hexToBytes(raw_bytes);
if (raw_bytes.length != 20)
return null;
raw_bytes.unshift(bitjs.pub);
let hash = Crypto.SHA256(Crypto.SHA256(raw_bytes, {
asBytes: true
}), {
asBytes: true
});
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)
floCrypto.isSameAddr = function (addr1, addr2) {
if (!addr1 || !addr2)
@ -299,8 +388,13 @@
raw2 = decodeAddress(addr2);
if (!raw1 || !raw2)
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;
}
}
const decodeAddress = floCrypto.decodeAddr = function (address) {
@ -320,7 +414,7 @@
hex: Crypto.util.bytesToHex(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);
if (decode) {
let bytes = decode.data;

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //floDapps v2.3.2d
(function (EXPORTS) { //floDapps v2.4.0
/* General functions for FLO Dapps*/
'use strict';
const floDapps = EXPORTS;
@ -144,11 +144,14 @@
}
});
var subAdmins, settings
var subAdmins, trustedIDs, settings;
Object.defineProperties(floGlobals, {
subAdmins: {
get: () => subAdmins
},
trustedIDs: {
get: () => trustedIDs
},
settings: {
get: () => settings
},
@ -181,6 +184,7 @@
credentials: {},
//for Dapps
subAdmins: {},
trustedIDs: {},
settings: {},
appObjects: {},
generalData: {},
@ -228,24 +232,53 @@
})
}
const startUpOptions = {
cloud: true,
app_config: true,
}
floDapps.startUpOptions = {
set app_config(val) {
if (val === true || val === false)
startUpOptions.app_config = val;
},
get app_config() { return startUpOptions.app_config },
set cloud(val) {
if (val === true || val === false)
startUpOptions.cloud = val;
},
get cloud() { return startUpOptions.cloud },
}
const startUpFunctions = [];
startUpFunctions.push(function readSupernodeListFromAPI() {
return new Promise((resolve, reject) => {
if (!startUpOptions.cloud)
return resolve("No cloud for this app");
compactIDB.readData("lastTx", floCloudAPI.SNStorageID, DEFAULT.root).then(lastTx => {
floBlockchainAPI.readData(floCloudAPI.SNStorageID, {
ignoreOld: lastTx,
sentOnly: true,
pattern: "SuperNodeStorage"
}).then(result => {
var query_options = { sentOnly: true, pattern: floCloudAPI.SNStorageName };
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
query_options.ignoreOld = lastTx;
else if (typeof lastTx == 'string') //lastTx is txid of last tx
query_options.after = lastTx;
//fetch data from flosight
floBlockchainAPI.readData(floCloudAPI.SNStorageID, query_options).then(result => {
for (var i = result.data.length - 1; i >= 0; i--) {
var content = JSON.parse(result.data[i]).SuperNodeStorage;
var content = JSON.parse(result.data[i])[floCloudAPI.SNStorageName];
for (let sn in content.removeNodes)
compactIDB.removeData("supernodes", sn, DEFAULT.root);
for (let sn in content.newNodes)
compactIDB.writeData("supernodes", content.newNodes[sn], sn, DEFAULT.root);
for (let sn in content.updateNodes)
compactIDB.readData("supernodes", sn, DEFAULT.root).then(r => {
r = r || {}
r.uri = content.updateNodes[sn];
compactIDB.writeData("supernodes", r, sn, DEFAULT.root);
});
}
compactIDB.writeData("lastTx", result.totalTxs, floCloudAPI.SNStorageID, DEFAULT.root);
compactIDB.writeData("lastTx", result.lastItem, floCloudAPI.SNStorageID, DEFAULT.root);
compactIDB.readAllData("supernodes", DEFAULT.root).then(nodes => {
floCloudAPI.init(nodes)
.then(result => resolve("Loaded Supernode list\n" + result))
@ -258,12 +291,16 @@
startUpFunctions.push(function readAppConfigFromAPI() {
return new Promise((resolve, reject) => {
if (!startUpOptions.app_config)
return resolve("No configs for this app");
compactIDB.readData("lastTx", `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root).then(lastTx => {
floBlockchainAPI.readData(DEFAULT.adminID, {
ignoreOld: lastTx,
sentOnly: true,
pattern: DEFAULT.application
}).then(result => {
var query_options = { sentOnly: true, pattern: DEFAULT.application };
if (typeof lastTx == 'number') //lastTx is tx count (*backward support)
query_options.ignoreOld = lastTx;
else if (typeof lastTx == 'string') //lastTx is txid of last tx
query_options.after = lastTx;
//fetch data from flosight
floBlockchainAPI.readData(DEFAULT.adminID, query_options).then(result => {
for (var i = result.data.length - 1; i >= 0; i--) {
var content = JSON.parse(result.data[i])[DEFAULT.application];
if (!content || typeof content !== "object")
@ -274,16 +311,25 @@
if (Array.isArray(content.addSubAdmin))
for (var k = 0; k < content.addSubAdmin.length; k++)
compactIDB.writeData("subAdmins", true, content.addSubAdmin[k]);
if (Array.isArray(content.removeTrustedID))
for (var j = 0; j < content.removeTrustedID.length; j++)
compactIDB.removeData("trustedIDs", content.removeTrustedID[j]);
if (Array.isArray(content.addTrustedID))
for (var k = 0; k < content.addTrustedID.length; k++)
compactIDB.writeData("trustedIDs", true, content.addTrustedID[k]);
if (content.settings)
for (let l in content.settings)
compactIDB.writeData("settings", content.settings[l], l)
}
compactIDB.writeData("lastTx", result.totalTxs, `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root);
compactIDB.writeData("lastTx", result.lastItem, `${DEFAULT.application}|${DEFAULT.adminID}`, DEFAULT.root);
compactIDB.readAllData("subAdmins").then(result => {
subAdmins = Object.keys(result);
compactIDB.readAllData("settings").then(result => {
settings = result;
resolve("Read app configuration from blockchain");
compactIDB.readAllData("trustedIDs").then(result => {
trustedIDs = Object.keys(result);
compactIDB.readAllData("settings").then(result => {
settings = result;
resolve("Read app configuration from blockchain");
})
})
})
})
@ -293,6 +339,8 @@
startUpFunctions.push(function loadDataFromAppIDB() {
return new Promise((resolve, reject) => {
if (!startUpOptions.cloud)
return resolve("No cloud for this app");
var loadData = ["appObjects", "generalData", "lastVC"]
var promises = []
for (var i = 0; i < loadData.length; i++)
@ -404,7 +452,8 @@
try {
user_public = floCrypto.getPubKeyHex(privKey);
user_id = floCrypto.getAddress(privKey);
floCloudAPI.user(user_id, privKey); //Set user for floCloudAPI
if (startUpOptions.cloud)
floCloudAPI.user(user_id, privKey); //Set user for floCloudAPI
user_priv_wrap = () => checkIfPinRequired(key);
let n = floCrypto.randInt(12, 20);
aes_key = floCrypto.randString(n);
@ -567,6 +616,8 @@
floDapps.manageAppConfig = function (adminPrivKey, addList, rmList, settings) {
return new Promise((resolve, reject) => {
if (!startUpOptions.app_config)
return reject("No configs for this app");
if (!Array.isArray(addList) || !addList.length) addList = undefined;
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
if (!settings || typeof settings !== "object" || !Object.keys(settings).length) settings = undefined;
@ -589,6 +640,30 @@
})
}
floDapps.manageAppTrustedIDs = function (adminPrivKey, addList, rmList) {
return new Promise((resolve, reject) => {
if (!startUpOptions.app_config)
return reject("No configs for this app");
if (!Array.isArray(addList) || !addList.length) addList = undefined;
if (!Array.isArray(rmList) || !rmList.length) rmList = undefined;
if (!addList && !rmList)
return reject("No change in list")
var floData = {
[DEFAULT.application]: {
addTrustedID: addList,
removeTrustedID: rmList
}
}
var floID = floCrypto.getFloID(adminPrivKey)
if (floID != DEFAULT.adminID)
reject('Access Denied for Admin privilege')
else
floBlockchainAPI.writeData(floID, JSON.stringify(floData), adminPrivKey)
.then(result => resolve(['Updated App Configuration', result]))
.catch(error => reject(error))
})
}
const clearCredentials = floDapps.clearCredentials = function () {
return new Promise((resolve, reject) => {
compactIDB.clearData('credentials', DEFAULT.application).then(result => {

File diff suppressed because one or more lines are too long

166
scripts/floTokenAPI.js Normal file
View File

@ -0,0 +1,166 @@
(function (EXPORTS) { //floTokenAPI v1.0.4a
/* Token Operator to send/receive tokens via blockchain using API calls*/
'use strict';
const tokenAPI = EXPORTS;
const DEFAULT = {
apiURL: floGlobals.tokenURL || "https://ranchimallflo.duckdns.org/",
currency: floGlobals.currency || "rupee"
}
Object.defineProperties(tokenAPI, {
URL: {
get: () => DEFAULT.apiURL
},
currency: {
get: () => DEFAULT.currency,
set: currency => DEFAULT.currency = currency
}
});
if (floGlobals.currency) tokenAPI.currency = floGlobals.currency;
Object.defineProperties(floGlobals, {
currency: {
get: () => DEFAULT.currency,
set: currency => DEFAULT.currency = currency
}
});
const fetch_api = tokenAPI.fetch = function (apicall) {
return new Promise((resolve, reject) => {
console.debug(DEFAULT.apiURL + apicall);
fetch(DEFAULT.apiURL + apicall).then(response => {
if (response.ok)
response.json().then(data => resolve(data));
else
reject(response)
}).catch(error => reject(error))
})
}
const getBalance = tokenAPI.getBalance = function (floID, token = DEFAULT.currency) {
return new Promise((resolve, reject) => {
fetch_api(`api/v1.0/getFloAddressBalance?token=${token}&floAddress=${floID}`)
.then(result => resolve(result.balance || 0))
.catch(error => reject(error))
})
}
tokenAPI.getTx = function (txID) {
return new Promise((resolve, reject) => {
fetch_api(`api/v1.0/getTransactionDetails/${txID}`).then(res => {
if (res.result === "error")
reject(res.description);
else if (!res.parsedFloData)
reject("Data piece (parsedFloData) missing");
else if (!res.transactionDetails)
reject("Data piece (transactionDetails) missing");
else
resolve(res);
}).catch(error => reject(error))
})
}
tokenAPI.sendToken = function (privKey, amount, receiverID, message = "", token = DEFAULT.currency, options = {}) {
return new Promise((resolve, reject) => {
let senderID = floCrypto.getFloID(privKey);
if (typeof amount !== "number" || isNaN(amount) || amount <= 0)
return reject("Invalid amount");
getBalance(senderID, token).then(bal => {
if (amount > bal)
return reject(`Insufficient ${token}# balance`);
floBlockchainAPI.writeData(senderID, `send ${amount} ${token}# ${message}`, privKey, receiverID, options)
.then(txid => resolve(txid))
.catch(error => reject(error))
}).catch(error => reject(error))
});
}
function sendTokens_raw(privKey, receiverID, token, amount, utxo, vout, scriptPubKey) {
return new Promise((resolve, reject) => {
var trx = bitjs.transaction();
trx.addinput(utxo, vout, scriptPubKey)
trx.addoutput(receiverID, floBlockchainAPI.sendAmt);
trx.addflodata(`send ${amount} ${token}#`);
var signedTxHash = trx.sign(privKey, 1);
floBlockchainAPI.broadcastTx(signedTxHash)
.then(txid => resolve([receiverID, txid]))
.catch(error => reject([receiverID, error]))
})
}
//bulk transfer tokens
tokenAPI.bulkTransferTokens = function (sender, privKey, token, receivers) {
return new Promise((resolve, reject) => {
if (typeof receivers !== 'object')
return reject("receivers must be object in format {receiver1: amount1, receiver2:amount2...}")
let receiver_list = Object.keys(receivers), amount_list = Object.values(receivers);
let invalidReceivers = receiver_list.filter(id => !floCrypto.validateFloID(id));
let invalidAmount = amount_list.filter(val => typeof val !== 'number' || val <= 0);
if (invalidReceivers.length)
return reject(`Invalid receivers: ${invalidReceivers}`);
else if (invalidAmount.length)
return reject(`Invalid amounts: ${invalidAmount}`);
if (receiver_list.length == 0)
return reject("Receivers cannot be empty");
if (receiver_list.length == 1) {
let receiver = receiver_list[0], amount = amount_list[0];
floTokenAPI.sendToken(privKey, amount, receiver, "", token)
.then(txid => resolve({ success: { [receiver]: txid } }))
.catch(error => reject(error))
} else {
//check for token balance
floTokenAPI.getBalance(sender, token).then(token_balance => {
let total_token_amout = amount_list.reduce((a, e) => a + e, 0);
if (total_token_amout > token_balance)
return reject(`Insufficient ${token}# balance`);
//split utxos
floBlockchainAPI.splitUTXOs(sender, privKey, receiver_list.length).then(split_txid => {
//wait for the split utxo to get confirmation
floBlockchainAPI.waitForConfirmation(split_txid).then(split_tx => {
//send tokens using the split-utxo
var scriptPubKey = split_tx.vout[0].scriptPubKey.hex;
let promises = [];
for (let i in receiver_list)
promises.push(sendTokens_raw(privKey, receiver_list[i], token, amount_list[i], split_txid, i, scriptPubKey));
Promise.allSettled(promises).then(results => {
let success = Object.fromEntries(results.filter(r => r.status == 'fulfilled').map(r => r.value));
let failed = Object.fromEntries(results.filter(r => r.status == 'rejected').map(r => r.reason));
resolve({ success, failed });
})
}).catch(error => reject(error))
}).catch(error => reject(error))
}).catch(error => reject(error))
}
})
}
tokenAPI.getAllTxs = function (floID, token = DEFAULT.currency) {
return new Promise((resolve, reject) => {
fetch_api(`api/v1.0/getFloAddressTransactions?token=${token}&floAddress=${floID}`)
.then(result => resolve(result))
.catch(error => reject(error))
})
}
const util = tokenAPI.util = {};
util.parseTxData = function (txData) {
let parsedData = {};
for (let p in txData.parsedFloData)
parsedData[p] = txData.parsedFloData[p];
parsedData.sender = txData.transactionDetails.vin[0].addr;
for (let vout of txData.transactionDetails.vout)
if (vout.scriptPubKey.addresses[0] !== parsedData.sender)
parsedData.receiver = vout.scriptPubKey.addresses[0];
parsedData.time = txData.transactionDetails.time;
return parsedData;
}
})('object' === typeof module ? module.exports : window.floTokenAPI = {});

View File

@ -1,4 +1,4 @@
(function (GLOBAL) { //lib v1.3.1
(function (GLOBAL) { //lib v1.4.2b
'use strict';
/* Utility Libraries required for Standard operations
* All credits for these codes belong to their respective creators, moderators and owners.
@ -4349,20 +4349,18 @@
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 */
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.multisig = 0x5e; //flochange - prefix for FLO Mainnet Multisig 0x5e
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 */
bitjs.privkey2wif = function (h) {
var r = Crypto.util.hexToBytes(h);
@ -4461,7 +4459,46 @@
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 = {};
btrx.version = 2; //flochange look at this version
btrx.inputs = [];
@ -4476,7 +4513,6 @@
'hash': txid,
'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.sequence = sequence || ((btrx.locktime == 0) ? 4294967295 : 0);
return this.inputs.push(o);
@ -4485,26 +4521,41 @@
btrx.addoutput = function (address, value) {
var o = {};
var buf = [];
var addrDecoded = btrx.addressDecode(address);
var addr = this.addressDecode(address);
o.value = new BigInteger('' + Math.round((value * 1) * 1e8), 10);
buf.push(118); //OP_DUP
buf.push(169); //OP_HASH160
buf.push(addrDecoded.length);
buf = buf.concat(addrDecoded); // address in bytes
buf.push(136); //OP_EQUALVERIFY
buf.push(172); // OP_CHECKSIG
if (addr.version === bitjs.pub) { // regular address
buf.push(118); //OP_DUP
buf.push(169); //OP_HASH160
buf = this.writeBytesToScriptBuffer(buf, addr.bytes);// address in bytes
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;
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 = txcomments;
return this.floData; //flochange .. returning the txcomments -- check if the function return will assign
this.floData = data;
return this.floData;
}
// Only standard addresses
// Only standard addresses (standard multisig supported)
btrx.addressDecode = function (address) {
var bytes = B58.decode(address);
var front = bytes.slice(0, bytes.length - 4);
@ -4515,7 +4566,10 @@
asBytes: true
}).slice(0, 4);
if (checksum + "" == back + "") {
return front.slice(1);
return {
version: front[0],
bytes: front.slice(1)
};
}
}
@ -4740,6 +4794,83 @@
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 */
btrx.signinput = function (index, wif, sigHashType) {
var key = bitjs.wif2pubkey(wif);
@ -4747,8 +4878,7 @@
var signature = this.transactionSig(index, wif, shType);
var buf = [];
var sigBytes = Crypto.util.hexToBytes(signature);
buf.push(sigBytes.length);
buf = buf.concat(sigBytes);
buf = this.writeBytesToScriptBuffer(buf, sigBytes);
var pubKeyBytes = Crypto.util.hexToBytes(key['pubkey']);
buf.push(pubKeyBytes.length);
buf = buf.concat(pubKeyBytes);
@ -4756,15 +4886,98 @@
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 */
btrx.sign = function (wif, sigHashType) {
var shType = sigHashType || 1;
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();
}
// 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 */
btrx.serialize = function () {
@ -4793,28 +5006,85 @@
}
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;
@ -4856,6 +5126,36 @@
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 */
bitjs.clone = function (obj) {
if (obj == null || typeof (obj) != 'object') return obj;
@ -5020,17 +5320,25 @@
//https://raw.github.com/bitcoinjs/bitcoinjs-lib/09e8c6e184d6501a0c2c59d73ca64db5c0d3eb95/src/address.js
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) {
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;
};
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.
@ -5059,7 +5367,7 @@
/**
* 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 hash = bytes.slice(0, 21);
var checksum = Crypto.SHA256(Crypto.SHA256(hash, {
@ -5075,11 +5383,12 @@
throw "Checksum validation failed!";
}
if (version != hash.shift()) {
/*if (version != hash.shift()) {
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
Bitcoin.ECDSA = (function () {
@ -6478,6 +6787,21 @@
};
}
//Return a Bech32 address for the multisig. Format is same as above
coinjs.pubkeys2MultisigAddressBech32 = function (pubkeys, required) {
var r = coinjs.pubkeys2MultisigAddress(pubkeys, required);
var program = Crypto.SHA256(Crypto.util.hexToBytes(r.redeemScript), {
asBytes: true
});
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
return {
'address': address,
'redeemScript': r.redeemScript,
'scripthash': Crypto.util.bytesToHex(program),
'size': r.size
};
}
/* new time locked address, provide the pubkey and time necessary to unlock the funds.
when time is greater than 500000000, it should be a unix timestamp (seconds since epoch),
otherwise it should be the block height required before this transaction can be released.
@ -6567,6 +6891,19 @@
};
}
coinjs.multisigBech32Address = function (redeemscript) {
var program = Crypto.SHA256(Crypto.util.hexToBytes(redeemscript), {
asBytes: true
});
var address = coinjs.bech32_encode(coinjs.bech32.hrp, [coinjs.bech32.version].concat(coinjs.bech32_convert(program, 8, 5, true)));
return {
'address': address,
'type': 'multisigBech32',
'redeemScript': redeemscript,
'scripthash': Crypto.util.bytesToHex(program)
};
}
/* extract the redeemscript from a bech32 address */
coinjs.bech32redeemscript = function (address) {
var r = false;
@ -6658,6 +6995,9 @@
} else if (o.version == coinjs.multisig) { // multisig address
o.type = 'multisig';
} else if (o.version == coinjs.multisigBech32) { // multisigBech32 added
o.type = 'multisigBech32';
} else if (o.version == coinjs.priv) { // wifkey
o.type = 'wifkey';
@ -6698,11 +7038,16 @@
}
} catch (e) {
let bech32rs = coinjs.bech32redeemscript(addr);
if (bech32rs) {
if (bech32rs && bech32rs.length == 40) {
return {
'type': 'bech32',
'redeemscript': bech32rs
};
} else if (bech32rs && bech32rs.length == 64) {
return {
'type': 'multisigBech32',
'redeemscript': bech32rs
};
} else {
return false;
}
@ -6711,7 +7056,7 @@
/* retreive the balance from a given address */
coinjs.addressBalance = function (address, callback) {
coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=addresses&request=bal&address=' + address + '&r=' + securedMathRandom(), callback, "GET");
coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=addresses&request=bal&address=' + address + '&r=' + Math.random(), callback, "GET");
}
/* decompress an compressed public key */
@ -7328,11 +7673,39 @@
return r;
}
/* decode the redeemscript of a multisignature transaction for Bech32*/
r.decodeRedeemScriptBech32 = function (script) {
var r = false;
try {
var s = coinjs.script(Crypto.util.hexToBytes(script));
if ((s.chunks.length >= 3) && s.chunks[s.chunks.length - 1] == 174) { //OP_CHECKMULTISIG
r = {};
r.signaturesRequired = s.chunks[0] - 80;
var pubkeys = [];
for (var i = 1; i < s.chunks.length - 2; i++) {
pubkeys.push(Crypto.util.bytesToHex(s.chunks[i]));
}
r.pubkeys = pubkeys;
var multi = coinjs.pubkeys2MultisigAddressBech32(pubkeys, r.signaturesRequired);
r.address = multi['address'];
r.type = 'multisig__'; // using __ for now to differentiat from the other object .type == "multisig"
var rs = Crypto.util.bytesToHex(s.buffer);
r.redeemscript = rs;
}
} catch (e) {
// console.log(e);
r = false;
}
return r;
}
/* create output script to spend */
r.spendToScript = function (address) {
var addr = coinjs.addressDecode(address);
var s = coinjs.script();
if (addr.type == "bech32") {
if (addr.type == "bech32" || addr.type == "multisigBech32") {
s.writeOp(0);
s.writeBytes(Crypto.util.hexToBytes(addr.redeemscript));
} else if (addr.version == coinjs.multisig) { // multisig address
@ -7490,12 +7863,12 @@
/* list unspent transactions */
r.listUnspent = function (address, callback) {
coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=addresses&request=unspent&address=' + address + '&r=' + securedMathRandom(), callback, "GET");
coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=addresses&request=unspent&address=' + address + '&r=' + Math.random(), callback, "GET");
}
/* list transaction data */
r.getTransaction = function (txid, callback) {
coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=bitcoin&request=gettransaction&txid=' + txid + '&r=' + securedMathRandom(), callback, "GET");
coinjs.ajax(coinjs.host + '?uid=' + coinjs.uid + '&key=' + coinjs.key + '&setmodule=bitcoin&request=gettransaction&txid=' + txid + '&r=' + Math.random(), callback, "GET");
}
/* add unspent to transaction */
@ -7525,7 +7898,7 @@
var n = u.getElementsByTagName("tx_output_n")[0].childNodes[0].nodeValue;
var scr = script || u.getElementsByTagName("script")[0].childNodes[0].nodeValue;
if (segwit) {
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.
It is not standard and removed during the signing procedure. */
@ -7664,7 +8037,7 @@
// start redeem script check
var extract = this.extractScriptKey(index);
if (extract['type'] != 'segwit') {
if (extract['type'] != 'segwit' && extract['type'] != 'multisig_bech32') {
return {
'result': 0,
'fail': 'redeemscript',
@ -7693,6 +8066,8 @@
scriptcode = scriptcode.slice(1);
scriptcode.unshift(25, 118, 169);
scriptcode.push(136, 172);
} else if (scriptcode[0] > 80) {
scriptcode.unshift(scriptcode.length)
}
var value = coinjs.numToBytes(extract['value'], 8);
@ -7855,11 +8230,26 @@
'signatures': 0,
'script': Crypto.util.bytesToHex(this.ins[index].script.buffer)
};
} else if (this.ins[index].script.chunks.length == 3 && this.ins[index].script.chunks[0][0] >= 80 && this.ins[index].script.chunks[0][this.ins[index].script.chunks[0].length - 1] == 174 && this.ins[index].script.chunks[1] == 0) { //OP_CHECKMULTISIG_BECH32
// multisig bech32 script
let last_index = this.ins[index].script.chunks.length - 1;
var value = -1;
if (last_index >= 2 && this.ins[index].script.chunks[last_index].length == 8) {
value = coinjs.bytesToNum(this.ins[index].script.chunks[last_index]); // value found encoded in transaction (THIS IS NON STANDARD)
}
var sigcount = (!this.witness[index]) ? 0 : this.witness[index].length - 2;
return {
'type': 'multisig_bech32',
'signed': 'false',
'signatures': sigcount,
'script': Crypto.util.bytesToHex(this.ins[index].script.chunks[0]),
'value': value
};
} else if (this.ins[index].script.chunks.length == 0) {
// empty
//bech32 witness check
var signed = ((this.witness[index]) && this.witness[index].length == 2) ? 'true' : 'false';
var sigs = (signed == 'true') ? 1 : 0;
var signed = ((this.witness[index]) && this.witness[index].length >= 2) ? 'true' : 'false';
var sigs = (signed == 'true') ? (!this.witness[index][0] ? this.witness[index].length - 2 : 1) : 0;
return {
'type': 'empty',
'signed': signed,
@ -8038,6 +8428,71 @@
return true;
}
r.signmultisig_bech32 = function (index, wif, sigHashType) {
function scriptListPubkey(redeemScript) {
var r = {};
for (var i = 1; i < redeemScript.chunks.length - 2; i++) {
r[i] = Crypto.util.hexToBytes(coinjs.pubkeydecompress(Crypto.util.bytesToHex(redeemScript.chunks[i])));
}
return r;
}
function scriptListSigs(sigList) {
let r = {};
var c = 0;
if (Array.isArray(sigList)) {
for (let i = 1; i < sigList.length - 1; i++) {
c++;
r[c] = Crypto.util.hexToBytes(sigList[i]);
}
}
return r;
}
var redeemScript = Crypto.util.bytesToHex(this.ins[index].script.chunks[0]); //redeemScript
if (!coinjs.isArray(this.witness)) {
this.witness = new Array(this.ins.length);
this.witness.fill([]);
}
var pubkeyList = scriptListPubkey(coinjs.script(redeemScript));
var sigsList = scriptListSigs(this.witness[index]);
let decode_rs = coinjs.script().decodeRedeemScriptBech32(redeemScript);
var shType = sigHashType || 1;
var txhash = this.transactionHashSegWitV0(index, shType);
if (txhash.result == 1 && decode_rs.pubkeys.includes(coinjs.wif2pubkey(wif)['pubkey'])) {
var segwitHash = Crypto.util.hexToBytes(txhash.hash);
var signature = Crypto.util.hexToBytes(this.transactionSig(index, wif, shType, segwitHash)); //CHECK THIS
sigsList[coinjs.countObject(sigsList) + 1] = signature;
var w = [];
for (let x in pubkeyList) {
for (let y in sigsList) {
var sighash = this.transactionHashSegWitV0(index, sigsList[y].slice(-1)[0] * 1).hash
sighash = Crypto.util.hexToBytes(sighash);
if (coinjs.verifySignature(sighash, sigsList[y], pubkeyList[x])) {
w.push((Crypto.util.bytesToHex(sigsList[y])))
}
}
}
// when enough signatures collected, remove any non standard data we store, i.e. input value
if (w.length >= decode_rs.signaturesRequired) {
this.ins[index].script = coinjs.script();
}
w.unshift(0);
w.push(redeemScript);
this.witness[index] = w;
}
}
/* sign a multisig input */
r.signmultisig = function (index, wif, sigHashType) {
@ -8187,6 +8642,9 @@
} else if (d['type'] == 'multisig') {
this.signmultisig(i, wif, shType);
} else if (d['type'] == 'multisig_bech32' && d['signed'] == "false") {
this.signmultisig_bech32(i, wif, shType);
} else if (d['type'] == 'segwit') {
this.signsegwit(i, wif, shType);
@ -8240,6 +8698,63 @@
return Crypto.util.bytesToHex(buffer);
}
//Utility funtion added to directly compute signatures without transaction index
r.transactionSigNoIndex = function (wif, sigHashType, txhash) {
function serializeSig(r, s) {
var rBa = r.toByteArraySigned();
var sBa = s.toByteArraySigned();
var sequence = [];
sequence.push(0x02); // INTEGER
sequence.push(rBa.length);
sequence = sequence.concat(rBa);
sequence.push(0x02); // INTEGER
sequence.push(sBa.length);
sequence = sequence.concat(sBa);
sequence.unshift(sequence.length);
sequence.unshift(0x30); // SEQUENCE
return sequence;
}
var shType = sigHashType || 1;
var hash = Crypto.util.hexToBytes(txhash);
if (hash) {
var curve = EllipticCurve.getSECCurveByName("secp256k1");
var key = coinjs.wif2privkey(wif);
var priv = BigInteger.fromByteArrayUnsigned(Crypto.util.hexToBytes(key['privkey']));
var n = curve.getN();
var e = BigInteger.fromByteArrayUnsigned(hash);
var badrs = 0
do {
var k = this.deterministicK(wif, hash, badrs);
var G = curve.getG();
var Q = G.multiply(k);
var r = Q.getX().toBigInteger().mod(n);
var s = k.modInverse(n).multiply(e.add(priv.multiply(r))).mod(n);
badrs++
} while (r.compareTo(BigInteger.ZERO) <= 0 || s.compareTo(BigInteger.ZERO) <= 0);
// Force lower s values per BIP62
var halfn = n.shiftRight(1);
if (s.compareTo(halfn) > 0) {
s = n.subtract(s);
};
var sig = serializeSig(r, s);
sig.push(parseInt(shType, 10));
return Crypto.util.bytesToHex(sig);
} else {
return false;
}
}
/* deserialize a transaction */
r.deserialize = function (buffer) {
if (typeof buffer == "string") {
@ -8582,12 +9097,116 @@
return count;
}
//Nine utility functions added for generating transaction hashes and verification of signatures
coinjs.changeEndianness = (string) => {
const result = [];
let len = string.length - 2;
while (len >= 0) {
result.push(string.substr(len, 2));
len -= 2;
}
return result.join('');
}
coinjs.getTransactionHash = function (transaction_in_hex, changeOutputEndianess) {
var x1, x2, x3, x4, x5;
x1 = Crypto.util.hexToBytes(transaction_in_hex);
x2 = Crypto.SHA256(x1);
x3 = Crypto.util.hexToBytes(x2);
x4 = Crypto.SHA256(x3);
x5 = coinjs.changeEndianness(x4);
if (changeOutputEndianess == true) { x5 = x5 } else if ((typeof changeOutputEndianess == 'undefined') || (changeOutputEndianess == false)) { x5 = x4 };
return x5;
}
coinjs.compressedToUncompressed = function (compressed) {
var t1, t2;
var curve = EllipticCurve.getSECCurveByName("secp256k1");
t1 = curve.curve.decodePointHex(compressed);
t2 = curve.curve.encodePointHex(t1);
return t2;
}
coinjs.uncompressedToCompressed = function (uncompressed) {
var t1, t2, t3;
t1 = uncompressed.charAt(uncompressed.length - 1)
t2 = parseInt(t1, 10);
//Check if the last digit is odd
if (t2 % 2 == 1) { t3 = "03"; } else { t3 = "02" };
return t3 + uncompressed.substr(2, 64);
}
coinjs.verifySignatureHex = function (hashHex, sigHex, pubHexCompressed) {
var h1, s1, p1, p2;
h1 = Crypto.util.hexToBytes(hashHex);
s1 = Crypto.util.hexToBytes(sigHex);
p1 = coinjs.compressedToUncompressed(pubHexCompressed);
p2 = Crypto.util.hexToBytes(p1);
return coinjs.verifySignature(h1, s1, p2);
}
coinjs.generateBitcoinSignature = function (private_key, hash, sighash_type_int = 1) {
var wif, tx1;
if (private_key.length < 60) { wif = private_key } else { wif = coinjs.privkey2wif(private_key) };
tx1 = coinjs.transaction();
return tx1.transactionSigNoIndex(wif, sighash_type_int, hash);
}
coinjs.dSHA256 = function (data) {
var t1, t2, t3;
t1 = Crypto.SHA256(Crypto.util.hexToBytes(data));
t2 = Crypto.util.hexToBytes(t1);
t3 = Crypto.SHA256(t2);
return t3;
}
coinjs.fromBitcoinAmountFormat = function (data) {
var x1, x2, x3;
x1 = coinjs.changeEndianness(data);
x2 = parseInt(x1, 16);
x3 = x2 / (10 ** 8);
return x3;
}
coinjs.toBitcoinAmountFormat = function (countBitcoin) {
var t2, t3, t4, t5;
t2 = countBitcoin * 10 ** 8;
t3 = t2.toString(16);
t4 = coinjs.changeEndianness(t3);
t5 = t4.padEnd(16, "0");
return t5;
}
coinjs.scriptcodeCreatorBasic = function (scriptpubkey) {
var t1, t2, t3, t4;
if (scriptpubkey.substr(0, 4) == "0014") {
//Scriptpubkey case
t1 = scriptpubkey.slice(2);
t2 = "1976a9" + t1 + "88ac";
} else {
//Redeemscript case
t3 = (scriptpubkey.length) / 2;
t4 = t3.toString(16);
t2 = t4 + scriptpubkey;
}
return t2;
}
coinjs.ripemd160sha256 = function (data) {
var t1, t2;
t1 = ripemd160(Crypto.SHA256(Crypto.util.hexToBytes(data), { asBytes: true }), { asBytes: true });
t2 = Crypto.util.bytesToHex(t1)
return t2;
}
coinjs.random = function (length) {
var r = "";
var l = length || 25;
var chars = "!$%^&*()_+{}:@~?><|\./;'#][=-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890";
for (let x = 0; x < l; x++) {
r += chars.charAt(Math.floor(securedMathRandom() * 62));
r += chars.charAt(Math.floor(Math.random() * 62));
}
return r;
}

5
scripts/lib.min.js vendored

File diff suppressed because one or more lines are too long

1
scripts/ribc.min.js vendored

File diff suppressed because one or more lines are too long