Merge branch 'master' of https://github.com/ranchimall/RIBC
This commit is contained in:
commit
270524b702
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.tmp*
|
||||
21
LICENCE
Normal file
21
LICENCE
Normal 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.
|
||||
20
index.html
20
index.html
@ -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>
|
||||
|
||||
@ -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 = {});
|
||||
|
||||
1
scripts/btcOperator.min.js
vendored
1
scripts/btcOperator.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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();
|
||||
|
||||
1
scripts/compactIDB.min.js
vendored
1
scripts/compactIDB.min.js
vendored
@ -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
1
scripts/floBlockchainAPI.min.js
vendored
1
scripts/floBlockchainAPI.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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
|
||||
},
|
||||
|
||||
1
scripts/floCloudAPI.min.js
vendored
1
scripts/floCloudAPI.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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;
|
||||
|
||||
1
scripts/floCrypto.min.js
vendored
1
scripts/floCrypto.min.js
vendored
File diff suppressed because one or more lines are too long
@ -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 => {
|
||||
|
||||
1
scripts/floDapps.min.js
vendored
1
scripts/floDapps.min.js
vendored
File diff suppressed because one or more lines are too long
166
scripts/floTokenAPI.js
Normal file
166
scripts/floTokenAPI.js
Normal 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 = {});
|
||||
747
scripts/lib.js
747
scripts/lib.js
@ -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
5
scripts/lib.min.js
vendored
File diff suppressed because one or more lines are too long
1
scripts/ribc.min.js
vendored
1
scripts/ribc.min.js
vendored
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user