|
@ -4,19 +4,17 @@ |
|
|
*/ |
|
|
*/ |
|
|
'use strict' |
|
|
'use strict' |
|
|
|
|
|
|
|
|
const addrHelper = require('../bitcoin/addresses-helper') |
|
|
|
|
|
const network = require('../bitcoin/network') |
|
|
const network = require('../bitcoin/network') |
|
|
const util = require('../util') |
|
|
const util = require('../util') |
|
|
const Logger = require('../logger') |
|
|
const Logger = require('../logger') |
|
|
const keys = require('../../keys')[network.key] |
|
|
const keys = require('../../keys')[network.key] |
|
|
const Sources = require('./sources') |
|
|
const Sources = require('./sources') |
|
|
const BitcoindWrapper = require('./bitcoind-wrapper') |
|
|
const BitcoindWrapper = require('./bitcoind-wrapper') |
|
|
const InsightWrapper = require('./insight-wrapper') |
|
|
|
|
|
const BtcComWrapper = require('./btccom-wrapper') |
|
|
const BtcComWrapper = require('./btccom-wrapper') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Remote data sources for testnet polled round-robin to spread load |
|
|
* Remote data sources for testnet |
|
|
*/ |
|
|
*/ |
|
|
class SourcesTestnet extends Sources { |
|
|
class SourcesTestnet extends Sources { |
|
|
|
|
|
|
|
@ -25,126 +23,22 @@ class SourcesTestnet extends Sources { |
|
|
*/ |
|
|
*/ |
|
|
constructor() { |
|
|
constructor() { |
|
|
super() |
|
|
super() |
|
|
this.sources = [] |
|
|
this._initSource() |
|
|
this.index = 0 |
|
|
|
|
|
this.sourceBech32 = null |
|
|
|
|
|
this.isBitcoindActive = false |
|
|
|
|
|
// Initializes external sources
|
|
|
|
|
|
this._initSources() |
|
|
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
/** |
|
|
/** |
|
|
* Initialize the external data sources |
|
|
* Initialize the external data source |
|
|
*/ |
|
|
*/ |
|
|
_initSources() { |
|
|
_initSource() { |
|
|
if (keys.explorers.bitcoind == 'active') { |
|
|
if (keys.explorers.bitcoind == 'active') { |
|
|
// If local bitcoind option is activated
|
|
|
// If local bitcoind option is activated
|
|
|
// we'll use the local node as our unique source
|
|
|
// we'll use the local node as our unique source
|
|
|
this.sourceBech32 = new BitcoindWrapper() |
|
|
this.source = new BitcoindWrapper() |
|
|
this.sources.push(this.sourceBech32) |
|
|
Logger.info('Activated Bitcoind as the data source for imports') |
|
|
this.isBitcoindActive = true |
|
|
|
|
|
} else { |
|
|
} else { |
|
|
// Otherwise, we use a set of insight servers + btc.com for bech32 addresses
|
|
|
// Otherwise, we'll use the rest api provided by OXT
|
|
|
this.sourceBech32 = new BtcComWrapper(keys.explorers.btccom) |
|
|
this.source = new BtcComWrapper(keys.explorers.btccom) |
|
|
for (let url of keys.explorers.insight) |
|
|
Logger.info('Activated BTC.COM API as the data source for imports') |
|
|
this.sources.push(new InsightWrapper(url)) |
|
|
|
|
|
this.isBitcoindActive = false |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Get the next source index |
|
|
|
|
|
* @returns {integer} returns the next source index |
|
|
|
|
|
*/ |
|
|
|
|
|
nextIndex() { |
|
|
|
|
|
this.index++ |
|
|
|
|
|
if (this.index >= this.sources.length) |
|
|
|
|
|
this.index = 0 |
|
|
|
|
|
return this.index |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* 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) { |
|
|
|
|
|
let source = '' |
|
|
|
|
|
|
|
|
|
|
|
const isBech32 = addrHelper.isBech32(address) |
|
|
|
|
|
|
|
|
|
|
|
const ret = { |
|
|
|
|
|
address, |
|
|
|
|
|
txids: [], |
|
|
|
|
|
ntx: 0 |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
source = isBech32 ? this.sourceBech32 : this.sources[this.nextIndex()] |
|
|
|
|
|
const result = await source.getAddress(address, filterAddr) |
|
|
|
|
|
|
|
|
|
|
|
if (result.ntx) |
|
|
|
|
|
ret.ntx = result.ntx |
|
|
|
|
|
else if (result.txids) |
|
|
|
|
|
ret.ntx = result.txids.length |
|
|
|
|
|
|
|
|
|
|
|
if (result.txids) |
|
|
|
|
|
ret.txids = result.txids |
|
|
|
|
|
|
|
|
|
|
|
return ret |
|
|
|
|
|
|
|
|
|
|
|
} catch(e) { |
|
|
|
|
|
Logger.error(e, `SourcesTestnet.getAddress() : ${address} from ${source.base}`) |
|
|
|
|
|
if (!isBech32 && this.sources.length > 1) { |
|
|
|
|
|
// Try again with another source
|
|
|
|
|
|
return this.getAddress(address, filterAddr) |
|
|
|
|
|
} else { |
|
|
|
|
|
return ret |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
|
* Retrieve information for a list of addresses |
|
|
|
|
|
* @param {string[]} addresses - array of 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 getAddresses(addresses, filterAddr) { |
|
|
|
|
|
const ret = [] |
|
|
|
|
|
|
|
|
|
|
|
try { |
|
|
|
|
|
if (this.isBitcoindActive) { |
|
|
|
|
|
const source = this.sources[0] |
|
|
|
|
|
const results = await source.getAddresses(addresses, filterAddr) |
|
|
|
|
|
for (let r of results) { |
|
|
|
|
|
// Filter addresses with too many txs
|
|
|
|
|
|
if (!filterAddr || (r.ntx <= keys.addrFilterThreshold)) |
|
|
|
|
|
ret.push(r) |
|
|
|
|
|
} |
|
|
|
|
|
} else { |
|
|
|
|
|
const lists = util.splitList(addresses, this.sources.length) |
|
|
|
|
|
await util.seriesCall(lists, async list => { |
|
|
|
|
|
const results = await Promise.all(list.map(a => { |
|
|
|
|
|
return this.getAddress(a, filterAddr) |
|
|
|
|
|
})) |
|
|
|
|
|
|
|
|
|
|
|
for (let r of results) { |
|
|
|
|
|
// Filter addresses with too many txs
|
|
|
|
|
|
if (!filterAddr || (r.ntx <= keys.addrFilterThreshold)) |
|
|
|
|
|
ret.push(r) |
|
|
|
|
|
} |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
Logger.error(e, `SourcesTestnet.getAddresses() : Addr list = ${addresses}`) |
|
|
|
|
|
} finally { |
|
|
|
|
|
return ret |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|