From 4c855b4569cb8c7618a3fe470028733551eb466e Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Sat, 21 Nov 2020 17:15:00 +0200 Subject: [PATCH 1/2] Explore wallet addresses using an embedded bwt electrum server --- .env-sample | 16 ++++++++-- README.md | 13 ++++++++ app.js | 6 ++++ app/api/addressApi.js | 6 ++-- app/api/bwtAddressApi.js | 67 ++++++++++++++++++++++++++++++++++++++++ views/address.pug | 6 ++-- 6 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 app/api/bwtAddressApi.js diff --git a/.env-sample b/.env-sample index 4059cb7ae..1317fa339 100644 --- a/.env-sample +++ b/.env-sample @@ -29,7 +29,7 @@ #BTCEXP_BITCOIND_RPC_TIMEOUT=5000 # Select optional "address API" to display address tx lists and balances -# Options: electrumx, blockchain.com, blockchair.com, blockcypher.com +# Options: electrumx, bwt, blockchain.com, blockchair.com, blockcypher.com # If electrumx set, the BTCEXP_ELECTRUMX_SERVERS variable must also be # set. # Default: none @@ -97,4 +97,16 @@ # Show tools list in a sub-nav at top of screen # Default: true -BTCEXP_UI_SHOW_TOOLS_SUBHEADER=true +#BTCEXP_UI_SHOW_TOOLS_SUBHEADER=true + +# Options for bwt +# See https://github.com/shesek/bwt for more info +# BTCEXP_BWT_XPUBS="xpub1;xpub2" +# BTCEXP_BWT_BARE_XPUBS="xpub1;xpub2" +# BTCEXP_BWT_DESCRIPTORS="wpkh(xpub1/*);pkh(xpub2/0/*)" +# BTCEXP_BWT_BITCOIND_WALLET= +# BTCEXP_BWT_RESCAN_SINCE= +# BTCEXP_BWT_VERBOSE=[0-4] +# BTCEXP_BWT_GAP_LIMIT=20 +# BTCEXP_BWT_INITIAL_IMPORT_SIZE=20 +# BTCEXP_BWT_POLL_INTERVAL=5 diff --git a/README.md b/README.md index 9e2a85db6..370a8fdfb 100644 --- a/README.md +++ b/README.md @@ -87,6 +87,19 @@ To match the features visible on the demo site at [https://explorer.btc21.org](h BTCEXP_SLOW_DEVICE_MODE=false # enables resource-intensive tasks (UTXO set query, 24hr volume querying) that are inappropriate for "slow" devices +#### Wallet addresses history + +Showing the history of your wallet addresses is supported via [bwt](https://github.com/sheske/bwt). +To enable this feature, install bwt with `npm install -g bwt-daemon` then start btc-rpc-explorer +with `--address-api bwt` and one or more `--descriptors`s or `--xpub`s. + +The wallet addresses will be derived and imported into Bitcoin Core. You can specify which wallet to use with `--bwt-bitcoind-wallet `. + +Scanning the full blockchain history for transactions involving your addresses may take up to 20-30 minutes. +To speed this up, you may specify a date to start rescanning from with `--bwt-rescan-since `, or +use `now` to watch for new transactions only. + +For example: `--address-api bwt --bwt-rescan-since 2020-01-01 --descriptor 'wpkh(xpub69..oP/0/*)' --xpub xpub66..zQ` ## Run via Docker diff --git a/app.js b/app.js index 343b0c7e5..bef03e099 100755 --- a/app.js +++ b/app.js @@ -45,6 +45,7 @@ var request = require("request"); var qrcode = require("qrcode"); var addressApi = require("./app/api/addressApi.js"); var electrumAddressApi = require("./app/api/electrumAddressApi.js"); +var bwtAddressApi = require("./app/api/bwtAddressApi.js"); var coreApi = require("./app/api/coreApi.js"); var auth = require('./app/auth.js'); var marked = require("marked"); @@ -271,6 +272,11 @@ function onRpcConnectionVerified(getnetworkinfo, getblockchaininfo) { // 1d / 7d volume refreshNetworkVolumes(); setInterval(refreshNetworkVolumes, 30 * 60 * 1000); + + if (config.addressApi == "bwt") { + bwtAddressApi.setup(config, global.activeBlockchain) + .catch(function(err) { utils.logError("bwt error", err) }); + } } function refreshUtxoSetSummary() { diff --git a/app/api/addressApi.js b/app/api/addressApi.js index 920e37d8d..23269b021 100644 --- a/app/api/addressApi.js +++ b/app/api/addressApi.js @@ -10,7 +10,7 @@ var blockchairAddressApi = require("./blockchairAddressApi.js"); var blockcypherAddressApi = require("./blockcypherAddressApi.js"); function getSupportedAddressApis() { - return ["blockchain.com", "blockchair.com", "blockcypher.com", "electrumx"]; + return ["blockchain.com", "blockchair.com", "blockcypher.com", "electrumx", "bwt"]; } function getCurrentAddressApiFeatureSupport() { @@ -35,7 +35,7 @@ function getCurrentAddressApiFeatureSupport() { sortAsc: false }; - } else if (config.addressApi == "electrumx") { + } else if (config.addressApi == "electrumx" || config.addressApi == "bwt") { return { pageNumbers: true, sortDesc: true, @@ -57,7 +57,7 @@ function getAddressDetails(address, scriptPubkey, sort, limit, offset) { } else if (config.addressApi == "blockcypher.com") { promises.push(blockcypherAddressApi.getAddressDetails(address, scriptPubkey, sort, limit, offset)); - } else if (config.addressApi == "electrumx") { + } else if (config.addressApi == "electrumx" || config.addressApi == "bwt") { promises.push(electrumAddressApi.getAddressDetails(address, scriptPubkey, sort, limit, offset)); } else { diff --git a/app/api/bwtAddressApi.js b/app/api/bwtAddressApi.js new file mode 100644 index 000000000..fe3c37dff --- /dev/null +++ b/app/api/bwtAddressApi.js @@ -0,0 +1,67 @@ +var debug = require("debug"); +var electrumAddressApi = require("./electrumAddressApi.js"); + +async function setup(config, activeBlockchain) { + try { var BwtDaemon = require('bwt-daemon'); } + catch (_) { throw new Error('The bwt backend requires installing the "bwt-daemon" package'); } + + var network = { main: 'bitcoin', test: 'testnet' }[activeBlockchain] || activeBlockchain; + var rpcCred = config.credentials.rpc; + var rpcUrl = `http://${rpcCred.host}:${rpcCred.port}`; + + var bwt = await BwtDaemon({ + network, + bitcoind_url: rpcUrl, + verbose: +debug.enabled('bwt'), + progress: reportProgress, + electrum: true, + + ...getEnvOptions(), + ...getAuthOptions(rpcCred), + ...getDescsXpubsOptions(), + }); + + var [ host, port ] = bwt.electrum_addr.split(':'); + config.electrumXServers = [{ host, port, protocol: 'tcp' }]; + + await electrumAddressApi.connectToServers(); +} + +function reportProgress(type, progress, { eta }) { + if (type == 'scan') console.log('[bwt] Rescan in progress... (%f%%, eta %d minutes)', (progress*100).toFixed(1), (eta/60).toFixed(1)) +} + +function getEnvOptions() { + return [ 'bitcoind_wallet', 'rescan_since', 'verbose', 'gap_limit', 'initial_import_size', 'poll_interval' ] + .reduce((O, name, idx) => { + var envVal = process.env[`BTCEXP_BWT_${name.toUpperCase()}`]; + if (envVal != null) O[name] = idx <= 1 ? envVal : +envVal; + return O; + }, {}); +} + +function getAuthOptions(rpcCred) { + return rpcCred.username + ? { bitcoind_auth: `${rpcCred.username}:${rpcCred.password}` } + : { bitcoind_cookie: rpcCred.cookie } +} + +function getDescsXpubsOptions() { + var options = { descriptors: [], xpubs: [], bare_xpubs: [] }; + + var argTypes = { '--descriptor': options.descriptors, '--xpub': options.xpubs, '--bare-xpub': options.bare_xpubs }; + process.argv.slice(0, -1).forEach((arg, i) => { + var tList = argTypes[arg]; + if (tList) tList.push(process.argv[i+1]); + }); + + var envTypes = { 'DESCRIPTORS': options.descriptors, 'XPUBS': options.xpubs, 'BARE_XPUBS': options.bare_xpubs }; + Object.entries(envTypes).forEach(([ envName, tList ]) => { + var envVal = process.env[`BTCEXP_BWT_${envName}`]; + if (envVal) tList.push(...envVal.split(';')) + }); + + return options; +} + +module.exports = { setup } diff --git a/views/address.pug b/views/address.pug index d2b3fdd05..ae10e7a03 100644 --- a/views/address.pug +++ b/views/address.pug @@ -90,7 +90,7 @@ block content div.card.shadow-sm.mb-3 div.card-body h3.h6.mb-0 Summary - if (config.addressApi) + if (config.addressApi && config.addressApi != "bwt") small.text-muted.border-dotted.ml-2(title=`Some details for this address were queried from ${config.addressApi}` data-toggle="tooltip") Trust Note hr @@ -252,7 +252,7 @@ block content if (config.addressApi) if (config.addressApi == "electrumx") small.text-muted.border-dotted.ml-2(title=`The list of transaction IDs for this address was queried from ElectrumX (using the configured server(s))` data-toggle="tooltip") Trust Note - else + else if (config.addressApi != "bwt") small.text-muted.border-dotted.ml-2(title=`The list of transaction IDs for this address was queried from ${config.addressApi}` data-toggle="tooltip") Trust Note if (!crawlerBot && txids && txids.length > 1 && addressApiSupport.sortDesc && addressApiSupport.sortAsc) @@ -318,6 +318,8 @@ block content else if (transactions.length == 0) span No transactions found + if (config.addressApi == "bwt") + p.text-muted btc-rpc-explorer is connected to a personal address index that provides history for wallet addresses only. each tx, txIndex in transactions //pre From ef51a12a67927c172d2056f54ccc51fb533dfc5c Mon Sep 17 00:00:00 2001 From: Nadav Ivgi Date: Thu, 14 Jan 2021 18:25:52 +0200 Subject: [PATCH 2/2] Update to the (now renamed) libbwt v0.2.1 --- README.md | 2 +- app/api/bwtAddressApi.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 370a8fdfb..b89910275 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ To match the features visible on the demo site at [https://explorer.btc21.org](h #### Wallet addresses history Showing the history of your wallet addresses is supported via [bwt](https://github.com/sheske/bwt). -To enable this feature, install bwt with `npm install -g bwt-daemon` then start btc-rpc-explorer +To enable this feature, install bwt with `npm install -g libbwt` then start btc-rpc-explorer with `--address-api bwt` and one or more `--descriptors`s or `--xpub`s. The wallet addresses will be derived and imported into Bitcoin Core. You can specify which wallet to use with `--bwt-bitcoind-wallet `. diff --git a/app/api/bwtAddressApi.js b/app/api/bwtAddressApi.js index fe3c37dff..fc740f333 100644 --- a/app/api/bwtAddressApi.js +++ b/app/api/bwtAddressApi.js @@ -2,8 +2,8 @@ var debug = require("debug"); var electrumAddressApi = require("./electrumAddressApi.js"); async function setup(config, activeBlockchain) { - try { var BwtDaemon = require('bwt-daemon'); } - catch (_) { throw new Error('The bwt backend requires installing the "bwt-daemon" package'); } + try { var { BwtDaemon } = require('libbwt'); } + catch (_) { throw new Error('The bwt backend requires installing the "libbwt" package'); } var network = { main: 'bitcoin', test: 'testnet' }[activeBlockchain] || activeBlockchain; var rpcCred = config.credentials.rpc; @@ -19,7 +19,7 @@ async function setup(config, activeBlockchain) { ...getEnvOptions(), ...getAuthOptions(rpcCred), ...getDescsXpubsOptions(), - }); + }).start(); var [ host, port ] = bwt.electrum_addr.split(':'); config.electrumXServers = [{ host, port, protocol: 'tcp' }];