Update floBlockchainAPI.js

This commit is contained in:
sairaj mote 2023-04-25 22:08:40 +05:30
parent adacfdcccd
commit 392157fecc

View File

@ -1,4 +1,4 @@
(function (EXPORTS) { //floBlockchainAPI v2.4.4 (function (EXPORTS) { //floBlockchainAPI v2.5.1
/* FLO Blockchain Operator to send/receive data from blockchain using API calls*/ /* FLO Blockchain Operator to send/receive data from blockchain using API calls*/
'use strict'; 'use strict';
const floBlockchainAPI = EXPORTS; const floBlockchainAPI = EXPORTS;
@ -21,6 +21,7 @@
util.Sat_to_FLO = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8)); util.Sat_to_FLO = value => parseFloat((value / SATOSHI_IN_BTC).toFixed(8));
util.FLO_to_Sat = value => parseInt(value * SATOSHI_IN_BTC); util.FLO_to_Sat = value => parseInt(value * SATOSHI_IN_BTC);
util.toFixed = value => parseFloat((value).toFixed(8));
Object.defineProperties(floBlockchainAPI, { Object.defineProperties(floBlockchainAPI, {
sendAmt: { sendAmt: {
@ -120,14 +121,46 @@
} }
//Get balance for the given Address //Get balance for the given Address
const getBalance = floBlockchainAPI.getBalance = function (addr) { const getBalance = floBlockchainAPI.getBalance = function (addr, after = null) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
promisedAPI(`api/addr/${addr}/balance`) let api = `api/addr/${addr}/balance`;
.then(balance => resolve(parseFloat(balance))) if (after) {
.catch(error => reject(error)); if (typeof after === 'string' && /^[0-9a-z]{64}$/i.test(after))
api += '?after=' + after;
else return reject("Invalid 'after' parameter");
}
promisedAPI(api).then(result => {
if (typeof result === 'object' && result.lastItem) {
getBalance(addr, result.lastItem)
.then(r => resolve(util.toFixed(r + result.data)))
.catch(error => reject(error))
} else resolve(result);
}).catch(error => reject(error))
}); });
} }
const getUTXOs = address => new Promise((resolve, reject) => {
promisedAPI(`api/addr/${address}/utxo`)
.then(utxo => resolve(utxo))
.catch(error => reject(error))
})
const getUnconfirmedSpent = address => new Promise((resolve, reject) => {
readTxs(address, { mempool: "only" }).then(result => {
let unconfirmedSpent = {};
for (let tx of result.items)
if (tx.confirmations == 0)
for (let vin of tx.vin)
if (vin.addr === address) {
if (Array.isArray(unconfirmedSpent[vin.txid]))
unconfirmedSpent[vin.txid].push(vin.vout);
else
unconfirmedSpent[vin.txid] = [vin.vout];
}
resolve(unconfirmedSpent);
}).catch(error => reject(error))
})
//create a transaction with single sender //create a transaction with single sender
const createTx = function (senderAddr, receiverAddr, sendAmt, floData = '', strict_utxo = true) { const createTx = function (senderAddr, receiverAddr, sendAmt, floData = '', strict_utxo = true) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -144,21 +177,8 @@
var fee = DEFAULT.fee; var fee = DEFAULT.fee;
if (balance < sendAmt + fee) if (balance < sendAmt + fee)
return reject("Insufficient FLO balance!"); return reject("Insufficient FLO balance!");
//get unconfirmed tx list getUnconfirmedSpent(senderAddr).then(unconfirmedSpent => {
promisedAPI(`api/addr/${senderAddr}`).then(result => { getUTXOs(senderAddr).then(utxos => {
readTxs(senderAddr, 0, result.unconfirmedTxApperances).then(result => {
let unconfirmedSpent = {};
for (let tx of result.items)
if (tx.confirmations == 0)
for (let vin of tx.vin)
if (vin.addr === senderAddr) {
if (Array.isArray(unconfirmedSpent[vin.txid]))
unconfirmedSpent[vin.txid].push(vin.vout);
else
unconfirmedSpent[vin.txid] = [vin.vout];
}
//get utxos list
promisedAPI(`api/addr/${senderAddr}/utxo`).then(utxos => {
//form/construct the transaction data //form/construct the transaction data
var trx = bitjs.transaction(); var trx = bitjs.transaction();
var utxoAmt = 0.0; var utxoAmt = 0.0;
@ -185,7 +205,6 @@
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error))
}) })
} }
@ -238,7 +257,7 @@
var trx = bitjs.transaction(); var trx = bitjs.transaction();
var utxoAmt = 0.0; var utxoAmt = 0.0;
var fee = DEFAULT.fee; var fee = DEFAULT.fee;
promisedAPI(`api/addr/${floID}/utxo`).then(utxos => { getUTXOs(floID).then(utxos => {
for (var i = utxos.length - 1; i >= 0; i--) for (var i = utxos.length - 1; i >= 0; i--)
if (utxos[i].confirmations) { if (utxos[i].confirmations) {
trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey); trx.addinput(utxos[i].txid, utxos[i].vout, utxos[i].scriptPubKey);
@ -405,7 +424,7 @@
//Get the UTXOs of the senders //Get the UTXOs of the senders
let promises = []; let promises = [];
for (let floID in senders) for (let floID in senders)
promises.push(promisedAPI(`api/addr/${floID}/utxo`)); promises.push(getUTXOs(floID));
Promise.all(promises).then(results => { Promise.all(promises).then(results => {
var trx = bitjs.transaction(); var trx = bitjs.transaction();
for (let floID in senders) { for (let floID in senders) {
@ -479,21 +498,8 @@
var fee = DEFAULT.fee; var fee = DEFAULT.fee;
if (balance < sendAmt + fee) if (balance < sendAmt + fee)
return reject("Insufficient FLO balance!"); return reject("Insufficient FLO balance!");
//get unconfirmed tx list getUnconfirmedSpent(senderAddr).then(unconfirmedSpent => {
promisedAPI(`api/addr/${senderAddr}`).then(result => { getUTXOs(senderAddr).then(utxos => {
readTxs(senderAddr, 0, result.unconfirmedTxApperances).then(result => {
let unconfirmedSpent = {};
for (let tx of result.items)
if (tx.confirmations == 0)
for (let vin of tx.vin)
if (vin.addr === senderAddr) {
if (Array.isArray(unconfirmedSpent[vin.txid]))
unconfirmedSpent[vin.txid].push(vin.vout);
else
unconfirmedSpent[vin.txid] = [vin.vout];
}
//get utxos list
promisedAPI(`api/addr/${senderAddr}/utxo`).then(utxos => {
//form/construct the transaction data //form/construct the transaction data
var trx = bitjs.transaction(); var trx = bitjs.transaction();
var utxoAmt = 0.0; var utxoAmt = 0.0;
@ -521,7 +527,6 @@
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error)) }).catch(error => reject(error))
}).catch(error => reject(error))
}); });
} }
@ -657,7 +662,7 @@
} }
const getTxOutput = (txid, i) => new Promise((resolve, reject) => { const getTxOutput = (txid, i) => new Promise((resolve, reject) => {
fetch_api(`api/tx/${txid}`) promisedAPI(`api/tx/${txid}`)
.then(result => resolve(result.vout[i])) .then(result => resolve(result.vout[i]))
.catch(error => reject(error)) .catch(error => reject(error))
}); });
@ -740,30 +745,57 @@
}) })
} }
const isUndefined = val => typeof val === 'undefined';
//Read Txs of Address between from and to //Read Txs of Address between from and to
const readTxs = floBlockchainAPI.readTxs = function (addr, from, to) { const readTxs = floBlockchainAPI.readTxs = function (addr, options = {}) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=${from}&to=${to}`) let api = `api/addrs/${addr}/txs`;
//API options
let api_options = [];
if (!isUndefined(options.after))
api_options.push(`after=${options.after}`);
else {
if (!isUndefined(options.from))
api_options.push(`from=${options.from}`);
if (!isUndefined(options.to))
api_options.push(`to=${options.to}`);
}
if (!isUndefined(options.mempool))
api_options.push(`mempool=${options.mempool}`)
if (api_options.length)
api += "?" + api_options.join('&');
promisedAPI(api)
.then(response => resolve(response)) .then(response => resolve(response))
.catch(error => reject(error)) .catch(error => reject(error))
}); });
} }
//Read All Txs of Address (newest first) //Read All Txs of Address (newest first)
floBlockchainAPI.readAllTxs = function (addr) { const readAllTxs = floBlockchainAPI.readAllTxs = function (addr, options) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { readTxs(addr, options).then(response => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=${response.totalItems}0`) if (response.incomplete) {
.then(response => resolve(response.items)) let next_options = Object.assign({}, options);
.catch(error => reject(error)); next_options.after = response.lastItem;
readAllTxs(addr, next_options).then(r => {
r.items = r.items.concat(response.items); //latest tx are 1st in array
resolve(r);
}).catch(error => reject(error)) }).catch(error => reject(error))
} else
resolve({
lastKey: response.lastItem || options.after,
items: response.items
});
})
}); });
} }
/*Read flo Data from txs of given Address /*Read flo Data from txs of given Address
options can be used to filter data options can be used to filter data
limit : maximum number of filtered data (default = 1000, negative = no limit) after : query after the given txid
ignoreOld : ignore old txs (default = 0) mempool : query mempool tx or not (options same as readAllTx, DEFAULT=false: ignore unconfirmed tx)
ignoreOld : ignore old txs (deprecated: support for backward compatibility only, cannot be used with 'after')
sentOnly : filters only sent data sentOnly : filters only sent data
receivedOnly: filters only received data receivedOnly: filters only received data
pattern : filters data that with JSON pattern pattern : filters data that with JSON pattern
@ -773,98 +805,73 @@
receiver : flo-id(s) of receiver receiver : flo-id(s) of receiver
*/ */
floBlockchainAPI.readData = function (addr, options = {}) { floBlockchainAPI.readData = function (addr, options = {}) {
options.limit = options.limit || 0; return new Promise((resolve, reject) => {
options.ignoreOld = options.ignoreOld || 0;
//fetch options
let fetch_options = {};
fetch_options.mempool = isUndefined(options.mempool) ? 'false' : options.mempool; //DEFAULT: ignore unconfirmed tx
if (!isUndefined(options.after)) {
if (!isUndefined(options.ignoreOld)) //Backward support
return reject("Invalid options: cannot use after and ignoreOld in same query");
else
fetch_options.after = options.after;
}
readAllTxs(addr, fetch_options).then(response => {
if (Number.isInteger(options.ignoreOld)) //backward support, cannot be used with options.after
response.items.splice(-options.ignoreOld); //negative to count from end of the array
if (typeof options.senders === "string") options.senders = [options.senders]; if (typeof options.senders === "string") options.senders = [options.senders];
if (typeof options.receivers === "string") options.receivers = [options.receivers]; if (typeof options.receivers === "string") options.receivers = [options.receivers];
return new Promise((resolve, reject) => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=1`).then(response => { //filter the txs based on options
var newItems = response.totalItems - options.ignoreOld; const filteredData = response.items.filter(tx => {
promisedAPI(`api/addrs/${addr}/txs?from=0&to=${newItems * 2}`).then(response => {
if (options.limit <= 0) if (!tx.confirmations) //unconfirmed transactions: this should not happen as we send mempool=false in API query
options.limit = response.items.length; return false;
var filteredData = [];
let numToRead = response.totalItems - options.ignoreOld, if (options.sentOnly && !tx.vin.some(vin => vin.addr === addr))
unconfirmedCount = 0; return false;
for (let i = 0; i < numToRead && filteredData.length < options.limit; i++) { else if (Array.isArray(options.senders) && !tx.vin.some(vin => options.senders.includes(vin.addr)))
if (!response.items[i].confirmations) { //unconfirmed transactions return false;
unconfirmedCount++;
if (numToRead < response.items[i].length) if (options.receivedOnly && !tx.vout.some(vout => vout.scriptPubKey.addresses[0] === addr))
numToRead++; return false;
continue; else if (Array.isArray(options.receivers) && !tx.vout.some(vout => options.receivers.includes(vout.scriptPubKey.addresses[0])))
} return false;
if (options.pattern) { if (options.pattern) {
try { try {
let jsonContent = JSON.parse(response.items[i].floData); let jsonContent = JSON.parse(tx.floData);
if (!Object.keys(jsonContent).includes(options.pattern)) if (!Object.keys(jsonContent).includes(options.pattern))
continue; return false;
} catch (error) { } catch {
continue; return false;
} }
} }
if (options.sentOnly) {
let flag = false;
for (let vin of response.items[i].vin)
if (vin.addr === addr) {
flag = true;
break;
}
if (!flag) continue;
}
if (Array.isArray(options.senders)) {
let flag = false;
for (let vin of response.items[i].vin)
if (options.senders.includes(vin.addr)) {
flag = true;
break;
}
if (!flag) continue;
}
if (options.receivedOnly) {
let flag = false;
for (let vout of response.items[i].vout)
if (vout.scriptPubKey.addresses[0] === addr) {
flag = true;
break;
}
if (!flag) continue;
}
if (Array.isArray(options.receivers)) {
let flag = false;
for (let vout of response.items[i].vout)
if (options.receivers.includes(vout.scriptPubKey.addresses[0])) {
flag = true;
break;
}
if (!flag) continue;
}
if (options.filter && !options.filter(response.items[i].floData))
continue;
if (options.tx) {
let d = {}
d.txid = response.items[i].txid;
d.time = response.items[i].time;
d.blockheight = response.items[i].blockheight;
d.senders = new Set(response.items[i].vin.map(v => v.addr));
d.receivers = new Set(response.items[i].vout.map(v => v.scriptPubKey.addresses[0]));
d.data = response.items[i].floData;
filteredData.push(d);
} else
filteredData.push(response.items[i].floData);
}
resolve({
totalTxs: response.totalItems - unconfirmedCount,
data: filteredData
});
}).catch(error => {
reject(error);
});
}).catch(error => {
reject(error);
});
});
}
if (options.filter && !options.filter(tx.floData))
return false;
return true;
}).map(tx => options.tx ? {
txid: tx.txid,
time: tx.time,
blockheight: tx.blockheight,
senders: new Set(tx.vin.map(v => v.addr)),
receivers: new Set(tx.vout.map(v => v.scriptPubKey.addresses[0])),
data: tx.floData
} : tx.floData);
const result = { lastKey: response.lastKey };
if (options.tx)
result.items = filteredData;
else
result.data = filteredData
resolve(result);
}).catch(error => reject(error))
})
}
})('object' === typeof module ? module.exports : window.floBlockchainAPI = {}); })('object' === typeof module ? module.exports : window.floBlockchainAPI = {});