Browse Source
add support of rest api provided by addrindexrs See merge request dojo/samourai-dojo!211umbrel
kenshin-samourai
4 years ago
7 changed files with 178 additions and 2 deletions
@ -0,0 +1,157 @@ |
|||
/*! |
|||
* lib/remote-importer/local-rest-indexer-wrapper.js |
|||
* Copyright © 2019 – Katana Cryptographic Ltd. All Rights Reserved. |
|||
*/ |
|||
'use strict' |
|||
|
|||
const axios = require('axios') |
|||
const bitcoin = require('bitcoinjs-lib') |
|||
const Logger = require('../logger') |
|||
const util = require('../util') |
|||
const network = require('../bitcoin/network') |
|||
const activeNet = network.network |
|||
const keys = require('../../keys')[network.key] |
|||
const Wrapper = require('./wrapper') |
|||
|
|||
|
|||
/** |
|||
* Wrapper for a local indexer |
|||
* providing a REST API |
|||
*/ |
|||
class LocalRestIndexerWrapper extends Wrapper { |
|||
|
|||
/** |
|||
* Constructor |
|||
*/ |
|||
constructor(url) { |
|||
super(url, null) |
|||
} |
|||
|
|||
/** |
|||
* 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' |
|||
} |
|||
} |
|||
|
|||
const result = await axios(params) |
|||
return result.data |
|||
} |
|||
|
|||
/** |
|||
* Translate a bitcoin address into a script hash |
|||
* (@see https://electrumx.readthedocs.io/en/latest/protocol-basics.html#script-hashes)
|
|||
* @param {string} address - bitcoin address |
|||
* @returns {string} returns the script hash associated to the address |
|||
*/ |
|||
_getScriptHash(address) { |
|||
const bScriptPubKey = bitcoin.address.toOutputScript(address, activeNet) |
|||
const bScriptHash = bitcoin.crypto.sha256(bScriptPubKey) |
|||
return bScriptHash.reverse().toString('hex') |
|||
} |
|||
|
|||
/** |
|||
* 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: [] |
|||
} |
|||
|
|||
const scriptHash = this._getScriptHash(address) |
|||
const uri = `/blockchain/scripthash/${scriptHash}/history` |
|||
const results = await this._get(uri) |
|||
|
|||
for (let r of results) { |
|||
ret.txids.push(r.tx_hash) |
|||
ret.ntx++ |
|||
} |
|||
|
|||
if (filterAddr && ret.ntx > keys.addrFilterThreshold) { |
|||
Logger.info(`Importer : Import of ${address} rejected (too many transactions - ${ret.ntx})`) |
|||
return { |
|||
address: address, |
|||
ntx: 0, |
|||
txids: [] |
|||
} |
|||
} |
|||
|
|||
return ret |
|||
} |
|||
|
|||
/** |
|||
* 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 = {} |
|||
const scriptHash2Address = {} |
|||
const scriptHashes = [] |
|||
|
|||
for (let a of addresses) { |
|||
const scriptHash = this._getScriptHash(a) |
|||
scriptHashes.push(scriptHash) |
|||
scriptHash2Address[scriptHash] = a |
|||
} |
|||
|
|||
const sScriptHashes = scriptHashes.join(',') |
|||
const uri = `/blockchain/scripthashes/history?scripthashes=${sScriptHashes}` |
|||
const results = await this._get(uri) |
|||
|
|||
for (let r of results) { |
|||
const a = scriptHash2Address[r.script_hash] |
|||
ret[a] = { |
|||
address: a, |
|||
ntx: r.txids.length, |
|||
txids: r.txids |
|||
} |
|||
} |
|||
|
|||
const aRet = Object.values(ret) |
|||
|
|||
for (let i in aRet) { |
|||
if (filterAddr && aRet[i].ntx > keys.addrFilterThreshold) { |
|||
Logger.info(`Importer : Import of ${aRet[i].address} rejected (too many transactions - ${aRet[i].ntx})`) |
|||
aRet.splice(i, 1) |
|||
} |
|||
} |
|||
|
|||
return aRet |
|||
} |
|||
|
|||
/** |
|||
* 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(`/blocks/tip`) |
|||
if (result != null && result['height'] != null) |
|||
chainTipHeight = parseInt(result['height']) |
|||
return {'chainTipHeight': chainTipHeight} |
|||
} |
|||
|
|||
} |
|||
|
|||
module.exports = LocalRestIndexerWrapper |
Loading…
Reference in new issue