You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

152 lines
4.0 KiB

/*!
* lib/remote-importer\esplora-wrapper.js
* Copyright © 2019 – Katana Cryptographic Ltd. All Rights Reserved.
*/
'use strict'
const axios = require('axios')
const addrHelper = require('../bitcoin/addresses-helper')
const util = require('../util')
const Logger = require('../logger')
const network = require('../bitcoin/network')
const keys = require('../../keys')[network.key]
const Wrapper = require('./wrapper')
/**
* Wrapper for the esplora block explorer APIs
*/
class EsploraWrapper extends Wrapper {
/**
* Constructor
* @constructor
* @param {string} url
*/
constructor(url) {
super(url, keys.indexer.socks5Proxy)
}
/**
* Send a GET request to the API
* @param {string} route
* @returns {Promise}
*/
async _get(route) {
const params = {
url: `${this.base}${route}`,
method: 'GET',
responseType: 'json',
timeout: 15000,
headers: {
'User-Agent': 'Dojo'
}
}
// Sets socks proxy agent if required
if (keys.indexer.socks5Proxy != null) {
params['httpAgent'] = this.socksProxyAgent
params['httpsAgent'] = this.socksProxyAgent
}
const result = await axios(params)
return result.data
}
/**
* Get a page of transactions related to a given address
* @param {string} address - bitcoin address
* @param {string} lastSeenTxid - last seen txid
* (see https://github.com/Blockstream/esplora/blob/master/API.md)
* @returns {Promise}
*/
async _getTxsForAddress(address, lastSeenTxid) {
let uri = `/api/address/${address}/txs`
if (lastSeenTxid)
uri = uri + `/chain/${lastSeenTxid}`
const results = await this._get(uri)
return results.map(tx => tx.txid)
}
/**
* Retrieve information for a given address
* @param {string} address - bitcoin address
* @param {boolean} filterAddr - True if an upper bound should be used
* for #transactions associated to the address, False otherwise
* @returns {Promise} returns an object
* { address: <bitcoin_address>, txids: <txids>, ntx: <total_nb_txs>}
*/
async getAddress(address, filterAddr) {
const ret = {
address: address,
ntx: 0,
txids: []
}
let lastSeenTxid = null
while (true) {
const txids = await this._getTxsForAddress(address, lastSeenTxid)
if (txids.length === 0)
// we have all the transactions
return ret
ret.txids = ret.txids.concat(txids)
ret.ntx += ret.txids.length
if (txids.length < EsploraWrapper.NB_TXS_PER_PAGE) {
// we have all the transactions
return ret
} else if (filterAddr && ret.ntx > keys.addrFilterThreshold) {
// we have too many transactions
Logger.info(`Importer : Import of ${ret.address} rejected (too many transactions - ${ret.ntx})`)
ret.txids = []
ret.ntx = 0
return ret
} else {
// we need a new iteration
lastSeenTxid = txids[txids.length-1]
}
}
}
/**
* Retrieve information for a given list of addresses
* @param {string[]} addresses - array of bitcoin addresses
* @param {boolean} filterAddr - True if an upper bound should be used
* for #transactions associated to the address, False otherwise
* @returns {Promise} returns an array of objects
* { address: <bitcoin_address>, txids: <txids>, ntx: <total_nb_txs>}
*/
async getAddresses(addresses, filterAddr) {
const ret = []
for (let a of addresses) {
const retAddr = await this.getAddress(a, filterAddr)
ret.push(retAddr)
}
return ret
}
/**
* Retrieve the height of the chaintip for the remote source
* @returns {Promise} returns an object
* {chainTipHeight: <chaintip_height>}
*/
async getChainTipHeight() {
let chainTipHeight = null
const result = await this._get(`/api/blocks/tip/height`)
if (result != null)
chainTipHeight = parseInt(result)
return {'chainTipHeight': chainTipHeight}
}
}
// Esplora returns a max of 25 txs per page
EsploraWrapper.NB_TXS_PER_PAGE = 25
module.exports = EsploraWrapper