From 37397d8ab19e04fb5e6b6832df75a87a552944e0 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Mon, 28 Sep 2020 18:32:19 +0200 Subject: [PATCH 01/54] prepare next iteration --- docker/my-dojo/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index f90324b..8dd6548 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -10,7 +10,7 @@ COMPOSE_CONVERT_WINDOWS_PATHS=1 -DOJO_VERSION_TAG=1.8.0 +DOJO_VERSION_TAG=1.9.0 DOJO_DB_VERSION_TAG=1.2.0 DOJO_BITCOIND_VERSION_TAG=1.8.0 DOJO_NODEJS_VERSION_TAG=1.8.0 From c9fe459eeef17be9a4cfe93667c96af8ef914aea Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 17:48:11 +0200 Subject: [PATCH 02/54] bump dojo version --- docker/my-dojo/.env | 2 +- keys/index-example.js | 4 ++-- package-lock.json | 2 +- package.json | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index 8dd6548..f42db71 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -13,7 +13,7 @@ COMPOSE_CONVERT_WINDOWS_PATHS=1 DOJO_VERSION_TAG=1.9.0 DOJO_DB_VERSION_TAG=1.2.0 DOJO_BITCOIND_VERSION_TAG=1.8.0 -DOJO_NODEJS_VERSION_TAG=1.8.0 +DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 DOJO_TOR_VERSION_TAG=1.5.0 DOJO_EXPLORER_VERSION_TAG=1.3.0 diff --git a/keys/index-example.js b/keys/index-example.js index 7e6c98a..902b4ad 100644 --- a/keys/index-example.js +++ b/keys/index-example.js @@ -15,7 +15,7 @@ module.exports = { /* * Dojo version */ - dojoVersion: '1.8.0', + dojoVersion: '1.9.0', /* * Bitcoind */ @@ -232,7 +232,7 @@ module.exports = { * Testnet parameters */ testnet: { - dojoVersion: '1.8.0', + dojoVersion: '1.9.0', bitcoind: { rpc: { user: 'user', diff --git a/package-lock.json b/package-lock.json index da87e85..aa42406 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "samourai-dojo", - "version": "1.8.0", + "version": "1.9.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index d2b4d14..e05353f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "samourai-dojo", - "version": "1.8.0", + "version": "1.9.0", "description": "Backend server for Samourai Wallet", "main": "accounts/index.js", "scripts": { From 1b005818ad1b27435002c084fbce2b2c8437b005 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 17:52:02 +0200 Subject: [PATCH 03/54] redirect to sign in page if auth as expired or is invalid --- .../dmt/addresses-tools/addresses-tools.js | 12 ++++------ .../admin/dmt/blocks-rescan/blocks-rescan.js | 3 +-- static/admin/dmt/index.html | 1 + static/admin/dmt/pairing/pairing.js | 3 +-- static/admin/dmt/pushtx/pushtx.js | 6 ++--- static/admin/dmt/status/status.js | 6 ++--- static/admin/dmt/txs-tools/txs-tools.js | 3 +-- static/admin/dmt/xpubs-tools/xpubs-tools.js | 12 ++++------ static/admin/index.html | 1 + static/admin/index.js | 3 +-- static/admin/lib/auth-utils.js | 19 +++++++++++---- static/admin/lib/errors-utils.js | 24 +++++++++++++++++++ static/admin/lib/messages.js | 9 ------- 13 files changed, 57 insertions(+), 45 deletions(-) create mode 100644 static/admin/lib/errors-utils.js diff --git a/static/admin/dmt/addresses-tools/addresses-tools.js b/static/admin/dmt/addresses-tools/addresses-tools.js index ccc302c..c03c3ed 100644 --- a/static/admin/dmt/addresses-tools/addresses-tools.js +++ b/static/admin/dmt/addresses-tools/addresses-tools.js @@ -30,8 +30,7 @@ const screenAddressesToolsScript = { lib_api.getExplorerPairingInfo().then(explorerInfo => { this.explorerInfo = explorerInfo }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, @@ -68,8 +67,7 @@ const screenAddressesToolsScript = { this.showImportForm(false) } }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) throw e }) }, @@ -83,8 +81,7 @@ const screenAddressesToolsScript = { lib_msg.displayInfo('Import complete') }) }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, @@ -97,8 +94,7 @@ const screenAddressesToolsScript = { lib_msg.displayInfo('Rescan complete') }) }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, diff --git a/static/admin/dmt/blocks-rescan/blocks-rescan.js b/static/admin/dmt/blocks-rescan/blocks-rescan.js index 0d47447..19b9bb8 100644 --- a/static/admin/dmt/blocks-rescan/blocks-rescan.js +++ b/static/admin/dmt/blocks-rescan/blocks-rescan.js @@ -32,8 +32,7 @@ const screenBlocksRescanScript = { const msg = `successfully rescanned blocks between height ${fromHeightRes} and height ${toHeightRes}` lib_msg.displayInfo(msg) }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }).then(() => { $('#rescan-from-height').val('') $('#rescan-to-height').val('') diff --git a/static/admin/dmt/index.html b/static/admin/dmt/index.html index 3140759..0237e01 100644 --- a/static/admin/dmt/index.html +++ b/static/admin/dmt/index.html @@ -15,6 +15,7 @@ + diff --git a/static/admin/dmt/pairing/pairing.js b/static/admin/dmt/pairing/pairing.js index 56d2ef6..4073473 100644 --- a/static/admin/dmt/pairing/pairing.js +++ b/static/admin/dmt/pairing/pairing.js @@ -27,8 +27,7 @@ const screenPairingScript = { lib_msg.cleanMessagesUi() return result }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) return result }) }, diff --git a/static/admin/dmt/pushtx/pushtx.js b/static/admin/dmt/pushtx/pushtx.js index 514d44d..1efbb00 100644 --- a/static/admin/dmt/pushtx/pushtx.js +++ b/static/admin/dmt/pushtx/pushtx.js @@ -29,8 +29,7 @@ const pushtxScript = { $('#pushed-uptime').text('-') $('#pushed-count').text('-') $('#pushed-amount').text('-') - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, @@ -48,8 +47,7 @@ const pushtxScript = { lib_msg.cleanMessagesUi() } }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, diff --git a/static/admin/dmt/status/status.js b/static/admin/dmt/status/status.js index e893b97..b1c1ee4 100644 --- a/static/admin/dmt/status/status.js +++ b/static/admin/dmt/status/status.js @@ -27,8 +27,7 @@ const statusScript = { $('#tracker-status-ind').css('color', '#f77c7c') $('#tracker-uptime').text('-') $('#tracker-chaintip').text('-') - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, @@ -58,8 +57,7 @@ const statusScript = { $('#node-network').text('-') $('#node-conn').text('-') $('#node-relay-fee').text('-') - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, diff --git a/static/admin/dmt/txs-tools/txs-tools.js b/static/admin/dmt/txs-tools/txs-tools.js index b6e992e..6bf7f0e 100644 --- a/static/admin/dmt/txs-tools/txs-tools.js +++ b/static/admin/dmt/txs-tools/txs-tools.js @@ -24,8 +24,7 @@ const screenTxsToolsScript = { lib_api.getExplorerPairingInfo().then(explorerInfo => { this.explorerInfo = explorerInfo }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.js b/static/admin/dmt/xpubs-tools/xpubs-tools.js index b8967d5..b179079 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.js +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.js @@ -32,8 +32,7 @@ const screenXpubsToolsScript = { lib_api.getExplorerPairingInfo().then(explorerInfo => { this.explorerInfo = explorerInfo }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, @@ -70,8 +69,7 @@ const screenXpubsToolsScript = { this.showImportForm(false) } }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) throw e }) }, @@ -101,8 +99,7 @@ const screenXpubsToolsScript = { lib_msg.displayInfo('Import complete') }) }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, @@ -119,8 +116,7 @@ const screenXpubsToolsScript = { lib_msg.displayInfo('Rescan complete') }) }).catch(e => { - lib_msg.displayErrors(lib_msg.extractJqxhrErrorMsg(e)) - console.log(e) + lib_errors.processError(e) }) }, diff --git a/static/admin/index.html b/static/admin/index.html index f66f05d..5073354 100644 --- a/static/admin/index.html +++ b/static/admin/index.html @@ -14,6 +14,7 @@ + diff --git a/static/admin/index.js b/static/admin/index.js index fd8980d..1349deb 100644 --- a/static/admin/index.js +++ b/static/admin/index.js @@ -33,8 +33,7 @@ function login() { } }, function (jqxhr) { - let msg = lib_msg.extractJqxhrErrorMsg(jqxhr) - lib_msg.displayErrors(msg) + lib_errors.processError(jqxhr) } ) } diff --git a/static/admin/lib/auth-utils.js b/static/admin/lib/auth-utils.js index d357971..656a3d7 100644 --- a/static/admin/lib/auth-utils.js +++ b/static/admin/lib/auth-utils.js @@ -9,6 +9,9 @@ const lib_auth = { /* SessionStorage Key used for refresh token */ SESSION_STORE_REFRESH_TOKEN: 'refresh_token', + /* SessionStorage Key used for the timestamp of the refresh token */ + SESSION_STORE_REFRESH_TOKEN_TS: 'refresh_token_ts', + /* JWT Scheme */ JWT_SCHEME: 'Bearer', @@ -43,6 +46,8 @@ const lib_auth = { * Stores refresh token in session storage */ setRefreshToken: function(token) { + const now = new Date(); + sessionStorage.setItem(this.SESSION_STORE_REFRESH_TOKEN_TS, now.getTime()) sessionStorage.setItem(this.SESSION_STORE_REFRESH_TOKEN, token) }, @@ -56,17 +61,23 @@ const lib_auth = { const now = new Date(); const atts = sessionStorage.getItem(this.SESSION_STORE_ACCESS_TOKEN_TS) - const timeElapsed = (now.getTime() - atts) / 1000 + let timeElapsed = (now.getTime() - atts) / 1000 // Refresh the access token if more than 5mn if (timeElapsed > 300) { - const dataJson = { - 'rt': this.getRefreshToken() + // Check if refresh token has expired or is about to expire + const rtts = sessionStorage.getItem(this.SESSION_STORE_REFRESH_TOKEN_TS) + if ((now.getTime() - rtts) / 1000 > 7200 - 60) { + // Force user to sign in again + this.logout() + return } let self = this - let deferred = lib_api.refreshToken(dataJson) + let deferred = lib_api.refreshToken({ + 'rt': this.getRefreshToken() + }) deferred.then( function (result) { diff --git a/static/admin/lib/errors-utils.js b/static/admin/lib/errors-utils.js new file mode 100644 index 0000000..e32c528 --- /dev/null +++ b/static/admin/lib/errors-utils.js @@ -0,0 +1,24 @@ +const lib_errors = { + + // Extract jqxhr error message + extractJqxhrErrorMsg: function(jqxhr) { + let hasErrorMsg = ('responseJSON' in jqxhr) && + (jqxhr['responseJSON'] != null) && + ('error' in jqxhr['responseJSON']) + + return hasErrorMsg ? jqxhr['responseJSON']['error'] : jqxhr.statusText + }, + + // Manage errors + processError: function(e) { + const errorMsg = this.extractJqxhrErrorMsg(e) + // Redirect to sign in page if authentication error + if (errorMsg == 'Invalid JSON Web Token' || errorMsg == 'Missing JSON Web Token') { + lib_auth.logout() + } else { + lib_msg.displayErrors(errorMsg) + console.log(e) + } + }, + +} diff --git a/static/admin/lib/messages.js b/static/admin/lib/messages.js index 8518491..b08adbb 100644 --- a/static/admin/lib/messages.js +++ b/static/admin/lib/messages.js @@ -1,14 +1,5 @@ const lib_msg = { - // Extracts jqxhr error message - extractJqxhrErrorMsg: function(jqxhr) { - let hasErrorMsg = ('responseJSON' in jqxhr) && - (jqxhr['responseJSON'] != null) && - ('error' in jqxhr['responseJSON']) - - return hasErrorMsg ? jqxhr['responseJSON']['error'] : jqxhr.statusText - }, - // UI functions addTextinID: function(text, id){ $(id).html(text.toUpperCase()) From d009be01f0aa1323ad5f8c09f7febcc33ca84ad6 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 17:58:30 +0200 Subject: [PATCH 04/54] update description of blocks rescan --- static/admin/dmt/blocks-rescan/blocks-rescan.html | 2 +- static/admin/dmt/welcome/welcome.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/static/admin/dmt/blocks-rescan/blocks-rescan.html b/static/admin/dmt/blocks-rescan/blocks-rescan.html index b99a52f..80738b3 100644 --- a/static/admin/dmt/blocks-rescan/blocks-rescan.html +++ b/static/admin/dmt/blocks-rescan/blocks-rescan.html @@ -1,7 +1,7 @@

BLOCKS RESCAN

-
Force the Tracker to rescan a range of blocks.
+
Force the Tracker to rescan a small range of blocks.
diff --git a/static/admin/dmt/welcome/welcome.html b/static/admin/dmt/welcome/welcome.html index 4584cda..ac78347 100644 --- a/static/admin/dmt/welcome/welcome.html +++ b/static/admin/dmt/welcome/welcome.html @@ -26,7 +26,7 @@ Check if a transaction is found in a block or in the mempool of your full node. BLOCKS RESCAN - Rescan the transactions confirmed by the blocks in a given range. + Force the Tracker to rescan a small range of blocks.
For large rescans you should prefer XPUB or address rescans.
HELP From a1a1055e4060d68b9351f5b6efaae1b187c64a2d Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 22:20:39 +0200 Subject: [PATCH 05/54] update messages displayed by addresses tool --- static/admin/dmt/addresses-tools/addresses-tools.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/static/admin/dmt/addresses-tools/addresses-tools.js b/static/admin/dmt/addresses-tools/addresses-tools.js index c03c3ed..c8c4dbb 100644 --- a/static/admin/dmt/addresses-tools/addresses-tools.js +++ b/static/admin/dmt/addresses-tools/addresses-tools.js @@ -73,7 +73,7 @@ const screenAddressesToolsScript = { }, importAddress: function() { - lib_msg.displayMessage('Processing address import...'); + lib_msg.displayMessage('Processing address import. Please wait...'); const jsonData = {'active': this.currentAddress} return lib_api.getWallet(jsonData) .then(result => { @@ -86,7 +86,7 @@ const screenAddressesToolsScript = { }, rescanAddress: function() { - lib_msg.displayMessage('Processing address rescan...'); + lib_msg.displayMessage('Processing address rescan. Please wait...'); return lib_api.getAddressRescan(this.currentAddress) .then(result => { this.hideRescanForm() From b06e57ee68a5dec0193cfa06687f375ad1a5586b Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 22:21:13 +0200 Subject: [PATCH 06/54] deactivate messages displayed by pushtx and status screens --- static/admin/dmt/pushtx/pushtx.js | 8 ++++---- static/admin/dmt/status/status.js | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/static/admin/dmt/pushtx/pushtx.js b/static/admin/dmt/pushtx/pushtx.js index 1efbb00..6472e3b 100644 --- a/static/admin/dmt/pushtx/pushtx.js +++ b/static/admin/dmt/pushtx/pushtx.js @@ -15,7 +15,7 @@ const pushtxScript = { }, refreshPushTxStatus: function() { - lib_msg.displayMessage('Loading PushTx status info...'); + //lib_msg.displayMessage('Loading PushTx status info...'); lib_api.getPushtxStatus().then(pushTxStatus => { if (pushTxStatus) { const data = pushTxStatus['data'] @@ -23,7 +23,7 @@ const pushtxScript = { $('#pushed-uptime').text(uptime) $('#pushed-count').text(data['push']['count']) $('#pushed-amount').text(data['push']['amount']) - lib_msg.cleanMessagesUi() + //lib_msg.cleanMessagesUi() } }).catch(e => { $('#pushed-uptime').text('-') @@ -34,7 +34,7 @@ const pushtxScript = { }, refreshScheduledTxsList: function() { - lib_msg.displayMessage('Loading PushTx orchestrator status info...'); + //lib_msg.displayMessage('Loading PushTx orchestrator status info...'); lib_api.getOrchestratorStatus().then(orchestrStatus => { if(orchestrStatus) { const data = orchestrStatus['data'] @@ -44,7 +44,7 @@ const pushtxScript = { this.processedSchedTxs.add(tx['schTxid']) } } - lib_msg.cleanMessagesUi() + //lib_msg.cleanMessagesUi() } }).catch(e => { lib_errors.processError(e) diff --git a/static/admin/dmt/status/status.js b/static/admin/dmt/status/status.js index b1c1ee4..d9e5e78 100644 --- a/static/admin/dmt/status/status.js +++ b/static/admin/dmt/status/status.js @@ -13,14 +13,14 @@ const statusScript = { }, refreshApiStatus: function() { - lib_msg.displayMessage('Loading API status info...'); + //lib_msg.displayMessage('Loading API status info...'); return lib_api.getApiStatus().then(apiStatus => { if (apiStatus) { $('#tracker-status-ind').html('✓') $('#tracker-status-ind').css('color', '#76d776') $('#tracker-uptime').text(apiStatus['uptime']) $('#tracker-chaintip').text(apiStatus['blocks']) - lib_msg.cleanMessagesUi() + //lib_msg.cleanMessagesUi() } }).catch(e => { $('#tracker-status-ind').text('X') @@ -32,7 +32,7 @@ const statusScript = { }, refreshPushTxStatus: function() { - lib_msg.displayMessage('Loading Tracker status info...'); + //lib_msg.displayMessage('Loading Tracker status info...'); lib_api.getPushtxStatus().then(pushTxStatus => { if (pushTxStatus) { const data = pushTxStatus['data'] @@ -46,7 +46,7 @@ const statusScript = { $('#node-network').text(network) $('#node-conn').text(data['bitcoind']['conn']) $('#node-relay-fee').text(data['bitcoind']['relayfee']) - lib_msg.cleanMessagesUi() + //lib_msg.cleanMessagesUi() } }).catch(e => { $('#node-status-ind').text('-') From 515eca1652c1159edb40ffd6e3083776e5eb0408 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 22:21:48 +0200 Subject: [PATCH 07/54] add getXpubRescanStatus() method to api wrapper --- static/admin/lib/api-wrapper.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/static/admin/lib/api-wrapper.js b/static/admin/lib/api-wrapper.js index 9a3b43b..47f492d 100644 --- a/static/admin/lib/api-wrapper.js +++ b/static/admin/lib/api-wrapper.js @@ -110,6 +110,14 @@ const lib_api = { ) }, + /** + * Gets the status of a xpub rescan + */ + getXpubRescanStatus: function(xpub) { + let uri = this.baseUri + '/xpub/' + xpub + '/import/status' + return this.sendGetUriEncoded(uri, {}) + }, + /** * Notifies the server of the new HD account for tracking. */ From 485cd12d3edba9ad5fc046fb7569aa49086d76e8 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 2 Oct 2020 22:22:26 +0200 Subject: [PATCH 08/54] improve management of timeouts during xpub imports and rescans --- static/admin/dmt/xpubs-tools/xpubs-tools.js | 53 +++++++++++++++++++-- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.js b/static/admin/dmt/xpubs-tools/xpubs-tools.js index b179079..7f02d1c 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.js +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.js @@ -3,6 +3,7 @@ const screenXpubsToolsScript = { explorerInfo: null, currentXpub: null, isReimport: false, + rescanStatusTimerId: null, initPage: function() { this.getExplorerInfo() @@ -75,7 +76,7 @@ const screenXpubsToolsScript = { }, importXpub: function() { - lib_msg.displayMessage('Processing xpub import...'); + lib_msg.displayMessage('Processing xpub import. Please wait...'); const jsonData = { 'xpub': this.currentXpub, @@ -95,31 +96,73 @@ const screenXpubsToolsScript = { return lib_api.postXpub(jsonData) .then(result => { + // Successful import this._searchXpub(this.currentXpub).then(() => { lib_msg.displayInfo('Import complete') }) }).catch(e => { - lib_errors.processError(e) + // Check if import has timeout'd or if we have an error + if (e['status'] == 502 || e['status'] == 504) { + // Wait for import completion + this.checkRescanStatus(() => { + this._searchXpub(this.currentXpub).then(() => { + lib_msg.displayInfo('Import complete') + }) + }) + } else { + lib_errors.processError(e) + } }) }, rescanXpub: function() { - lib_msg.displayMessage('Processing xpub rescan...'); + lib_msg.displayMessage('Processing xpub rescan. Please wait...'); let startIdx = $('#rescan-start-idx').val() startIdx = (startIdx == null) ? 0 : parseInt(startIdx) let lookahead = $('#rescan-lookahead').val() lookahead = (lookahead == null) ? 100 : parseInt(lookahead) return lib_api.getXpubRescan(this.currentXpub, lookahead, startIdx) - .then(result => { + .then(() => { + // Successful rescan this.hideRescanForm() this._searchXpub(this.currentXpub).then(() => { lib_msg.displayInfo('Rescan complete') }) }).catch(e => { - lib_errors.processError(e) + // Check if rescan has timeout'd or if we have an error + if (e['status'] == 502 || e['status'] == 504) { + // Wait for rescan completion + this.checkRescanStatus(() => { + this.hideRescanForm() + this._searchXpub(this.currentXpub).then(() => { + lib_msg.displayInfo('Rescan complete') + }) + }) + } else { + lib_errors.processError(e) + } }) }, + checkRescanStatus: function(callback) { + this.rescanStatusTimerId = setTimeout(() => { + lib_api.getXpubRescanStatus(this.currentXpub) + .then(result => { + if (result['data']['import_in_progress']) { + lib_msg.displayMessage('Rescan in progress. Please wait...'); + return this.checkRescanStatus(callback) + } else { + clearTimeout(this.rescanStatusTimerId) + return callback() + } + }).catch(e => { + lib_errors.processError(e) + lib_msg.displayMessage('Rescan in progress. Please wait...'); + return this.checkRescanStatus(callback) + }) + }, 10000) + }, + setXpubDetails: function(xpubInfo) { $('tr.tx-row').remove() $('tr.utxo-row').remove() From bf7ae27909b79ff53a440f78ed9b406a7ab8c63f Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Mon, 5 Oct 2020 15:36:03 +0200 Subject: [PATCH 09/54] add new config property BITCOIND_LISTEN_MODE in bitcoind.conf --- docker/my-dojo/bitcoin/restart.sh | 9 ++++++--- docker/my-dojo/conf/docker-bitcoind.conf.tpl | 5 +++++ docker/my-dojo/docker-compose.yaml | 1 + docker/my-dojo/dojo.sh | 14 +++++++++----- docker/my-dojo/tor/restart.sh | 13 +++++++++---- 5 files changed, 30 insertions(+), 12 deletions(-) diff --git a/docker/my-dojo/bitcoin/restart.sh b/docker/my-dojo/bitcoin/restart.sh index 751bd6b..d8cbf98 100644 --- a/docker/my-dojo/bitcoin/restart.sh +++ b/docker/my-dojo/bitcoin/restart.sh @@ -4,15 +4,12 @@ set -e echo "## Start bitcoind #############################" bitcoind_options=( - -bind=172.28.1.5 -datadir=/home/bitcoin/.bitcoin -printtoconsole=1 -dbcache=$BITCOIND_DB_CACHE -disablewallet=1 -dns=$BITCOIND_DNS -dnsseed=$BITCOIND_DNSSEED - -externalip=$(cat /var/lib/tor/hsv2bitcoind/hostname) - -listen=1 -maxconnections=$BITCOIND_MAX_CONNECTIONS -maxmempool=$BITCOIND_MAX_MEMPOOL -mempoolexpiry=$BITCOIND_MEMPOOL_EXPIRY @@ -32,6 +29,12 @@ bitcoind_options=( -zmqpubrawtx=tcp://0.0.0.0:9501 ) +if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + bitcoind_options+=(-listen=1) + bitcoind_options+=(-bind=172.28.1.5) + bitcoind_options+=(-externalip=$(cat /var/lib/tor/hsv2bitcoind/hostname)) +fi + if [ "$BITCOIND_RPC_EXTERNAL" == "on" ]; then bitcoind_options+=(-zmqpubhashtx=tcp://0.0.0.0:9500) bitcoind_options+=(-zmqpubrawblock=tcp://0.0.0.0:9503) diff --git a/docker/my-dojo/conf/docker-bitcoind.conf.tpl b/docker/my-dojo/conf/docker-bitcoind.conf.tpl index 2695e14..6a19192 100644 --- a/docker/my-dojo/conf/docker-bitcoind.conf.tpl +++ b/docker/my-dojo/conf/docker-bitcoind.conf.tpl @@ -39,6 +39,11 @@ BITCOIND_MEMPOOL_EXPIRY=72 # Type: numeric BITCOIND_MIN_RELAY_TX_FEE=0.00001 +# Allow incoming connections +# This parameter is inactive if BITCOIND_INSTALL is set to 'off' +# Values: on | off +BITCOIND_LISTEN_MODE=on + # # EXPERT SETTINGS diff --git a/docker/my-dojo/docker-compose.yaml b/docker/my-dojo/docker-compose.yaml index 1f1638b..197ce65 100644 --- a/docker/my-dojo/docker-compose.yaml +++ b/docker/my-dojo/docker-compose.yaml @@ -92,6 +92,7 @@ services: context: ./tor env_file: - ./.env + - ./conf/docker-bitcoind.conf - ./conf/docker-explorer.conf - ./conf/docker-whirlpool.conf - ./conf/docker-tor.conf diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index cda0d74..ff39c7a 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -83,8 +83,10 @@ stop() { # Shutdown the bitcoin daemon if [ "$BITCOIND_INSTALL" == "on" ]; then # Renewal of bitcoind onion address - if [ "$BITCOIND_EPHEMERAL_HS" = "on" ]; then - $( docker exec -it tor rm -rf /var/lib/tor/hsv2bitcoind ) &> /dev/null + if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + if [ "$BITCOIND_EPHEMERAL_HS" = "on" ]; then + $( docker exec -it tor rm -rf /var/lib/tor/hsv2bitcoind ) &> /dev/null + fi fi # Stop the bitcoin daemon $( docker exec -it bitcoind bitcoin-cli \ @@ -360,9 +362,11 @@ onion() { fi if [ "$BITCOIND_INSTALL" == "on" ]; then - V2_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv2bitcoind/hostname ) - echo "Your local bitcoind (do not share) = $V2_ADDR_BTCD" - echo " " + if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + V2_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv2bitcoind/hostname ) + echo "Your local bitcoind (do not share) = $V2_ADDR_BTCD" + echo " " + fi fi } diff --git a/docker/my-dojo/tor/restart.sh b/docker/my-dojo/tor/restart.sh index 1cf4458..e44a98b 100644 --- a/docker/my-dojo/tor/restart.sh +++ b/docker/my-dojo/tor/restart.sh @@ -19,12 +19,17 @@ tor_options=( --HiddenServiceDir /var/lib/tor/hsv3dojo --HiddenServiceVersion 3 --HiddenServicePort "80 172.29.1.3:80" - --HiddenServiceDir /var/lib/tor/hsv2bitcoind - --HiddenServiceVersion 2 - --HiddenServicePort "8333 172.28.1.5:8333" - --HiddenServiceDirGroupReadable 1 ) +if [ "$BITCOIND_INSTALL" == "on" ]; then + if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv2bitcoind) + tor_options+=(--HiddenServiceVersion 2) + tor_options+=(--HiddenServicePort "8333 172.28.1.5:8333") + tor_options+=(--HiddenServiceDirGroupReadable 1) + fi +fi + if [ "$EXPLORER_INSTALL" == "on" ]; then tor_options+=(--HiddenServiceDir /var/lib/tor/hsv3explorer) tor_options+=(--HiddenServiceVersion 3) From 345b5b061c85f8d3511b1c833beb9108eee588df Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Mon, 5 Oct 2020 15:36:25 +0200 Subject: [PATCH 10/54] bump version of bitcoind and tor containers --- docker/my-dojo/.env | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index f42db71..74bd3bb 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -12,10 +12,10 @@ COMPOSE_CONVERT_WINDOWS_PATHS=1 DOJO_VERSION_TAG=1.9.0 DOJO_DB_VERSION_TAG=1.2.0 -DOJO_BITCOIND_VERSION_TAG=1.8.0 +DOJO_BITCOIND_VERSION_TAG=1.9.0 DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 -DOJO_TOR_VERSION_TAG=1.5.0 +DOJO_TOR_VERSION_TAG=1.6.0 DOJO_EXPLORER_VERSION_TAG=1.3.0 DOJO_INDEXER_VERSION_TAG=1.1.0 DOJO_WHIRLPOOL_VERSION_TAG=1.2.0 From f426427b72586931f7bc06da91cbcf1d035a2630 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Tue, 6 Oct 2020 13:30:33 +0200 Subject: [PATCH 11/54] upgrade explorer to btc-rpc-explorer 2.0.2 --- docker/my-dojo/.env | 2 +- docker/my-dojo/explorer/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index 74bd3bb..eb58d5b 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -16,7 +16,7 @@ DOJO_BITCOIND_VERSION_TAG=1.9.0 DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 DOJO_TOR_VERSION_TAG=1.6.0 -DOJO_EXPLORER_VERSION_TAG=1.3.0 +DOJO_EXPLORER_VERSION_TAG=1.4.0 DOJO_INDEXER_VERSION_TAG=1.1.0 DOJO_WHIRLPOOL_VERSION_TAG=1.2.0 diff --git a/docker/my-dojo/explorer/Dockerfile b/docker/my-dojo/explorer/Dockerfile index 8f32102..23ac535 100644 --- a/docker/my-dojo/explorer/Dockerfile +++ b/docker/my-dojo/explorer/Dockerfile @@ -3,7 +3,7 @@ FROM node:12-buster ENV APP_DIR /home/node/app ENV EXPLORER_URL https://github.com/janoside/btc-rpc-explorer/archive -ENV EXPLORER_VERSION 2.0.0 +ENV EXPLORER_VERSION 2.0.2 # Install netcat RUN set -ex && \ From 08e8e82f403f3babe3e1a373aa4951c58f874bad Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 9 Oct 2020 15:46:03 +0000 Subject: [PATCH 12/54] add new getChaintipHeight() method to remote importer and data sources --- lib/indexer-rpc/rpc-client.js | 6 ++-- lib/remote-importer/bitcoind-wrapper.js | 14 ++++++-- lib/remote-importer/esplora-wrapper.js | 15 +++++++- lib/remote-importer/local-indexer-wrapper.js | 29 ++++++++++++--- lib/remote-importer/oxt-wrapper.js | 17 +++++++-- lib/remote-importer/remote-importer.js | 37 ++++++++++++-------- lib/remote-importer/sources.js | 18 +++++++++- 7 files changed, 108 insertions(+), 28 deletions(-) diff --git a/lib/indexer-rpc/rpc-client.js b/lib/indexer-rpc/rpc-client.js index 6982d3c..eb324ea 100644 --- a/lib/indexer-rpc/rpc-client.js +++ b/lib/indexer-rpc/rpc-client.js @@ -181,14 +181,14 @@ class RpcClient { throw new Error(JSON.stringify(parsed.error)) // Add the parsed reponse to the array of responses if (batched) { - responses = parsed.map(p => { return {idxAddr: p.id, txs: p.result} }) + responses = parsed.map(p => { return {id: p.id, response: p.result} }) } else { - responses.push({idxAddr: parsed.id, txs: parsed.result}) + responses.push({id: parsed.id, response: parsed.result}) } // Reset the response response = '' // If all responses have been received - // close the connection + // close the connection if (responses.length == data.length) conn.end() } catch (err) { diff --git a/lib/remote-importer/bitcoind-wrapper.js b/lib/remote-importer/bitcoind-wrapper.js index e578bbf..c66e52d 100644 --- a/lib/remote-importer/bitcoind-wrapper.js +++ b/lib/remote-importer/bitcoind-wrapper.js @@ -6,6 +6,7 @@ const bitcoin = require('bitcoinjs-lib') const RpcClient = require('../bitcoind-rpc/rpc-client') +const rpcLatestBlock = require('../bitcoind-rpc/latest-block') const Logger = require('../logger') const network = require('../bitcoin/network') const activeNet = network.network @@ -77,7 +78,7 @@ class BitcoindWrapper extends Wrapper { txids: [] } } - + return ret } @@ -113,7 +114,7 @@ class BitcoindWrapper extends Wrapper { } 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})`) @@ -124,6 +125,15 @@ class BitcoindWrapper extends Wrapper { return aRet } + /** + * Retrieve the height of the chaintip for the remote source + * @returns {Promise} returns an object + * {chainTipHeight: } + */ + async getChainTipHeight() { + return {'chainTipHeight': rpcLatestBlock.height} + } + } module.exports = BitcoindWrapper diff --git a/lib/remote-importer/esplora-wrapper.js b/lib/remote-importer/esplora-wrapper.js index 8376179..98e4b71 100644 --- a/lib/remote-importer/esplora-wrapper.js +++ b/lib/remote-importer/esplora-wrapper.js @@ -37,7 +37,7 @@ class EsploraWrapper extends Wrapper { json: true, timeout: 15000 } - + // Sets socks proxy agent if required if (keys.indexer.socks5Proxy != null) params['agent'] = this.socksProxyAgent @@ -123,6 +123,19 @@ class EsploraWrapper extends Wrapper { return ret } + /** + * Retrieve the height of the chaintip for the remote source + * @returns {Promise} returns an object + * {chainTipHeight: } + */ + 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 diff --git a/lib/remote-importer/local-indexer-wrapper.js b/lib/remote-importer/local-indexer-wrapper.js index 5e706ae..dbf480f 100644 --- a/lib/remote-importer/local-indexer-wrapper.js +++ b/lib/remote-importer/local-indexer-wrapper.js @@ -16,7 +16,7 @@ const Wrapper = require('./wrapper') /** * Wrapper for a local indexer * Currently supports indexers - * providing a RPC API compliant + * providing a RPC API compliant * with a subset of the electrum protocol */ class LocalIndexerWrapper extends Wrapper { @@ -64,7 +64,7 @@ class LocalIndexerWrapper extends Wrapper { scriptHash ) - for (let r of results.txs) { + for (let r of results.response) { ret.txids.push(r.tx_hash) ret.ntx++ } @@ -77,7 +77,7 @@ class LocalIndexerWrapper extends Wrapper { txids: [] } } - + return ret } @@ -109,8 +109,8 @@ class LocalIndexerWrapper extends Wrapper { : await this.client.sendRequests(commands) for (let r of results) { - const addr = addresses[r.idxAddr] - const txids = r.txs.map(t => t.tx_hash) + const addr = addresses[r.id] + const txids = r.response.map(t => t.tx_hash) ret[addr] = { address: addr, @@ -131,12 +131,31 @@ class LocalIndexerWrapper extends Wrapper { return aRet } + /** + * Retrieve the height of the chaintip for the remote source + * @returns {Promise} returns an object + * {chainTipHeight: } + */ + async getChainTipHeight() { + let chainTipHeight = null + const result = await this.client.sendRequest( + LocalIndexerWrapper.HEADERS_SUBSCRIBE_RPC_CMD, + null + ) + if (result != null && result['response'] != null && result['response']['height'] != null) + chainTipHeight = parseInt(result['response']['height']) + return {'chainTipHeight': chainTipHeight} + } } /** * Get history RPC command (Electrum protocol) */ LocalIndexerWrapper.GET_HISTORY_RPC_CMD = 'blockchain.scripthash.get_history' +/** + * Get history RPC command (Electrum protocol) + */ +LocalIndexerWrapper.HEADERS_SUBSCRIBE_RPC_CMD = 'blockchain.headers.subscribe' module.exports = LocalIndexerWrapper diff --git a/lib/remote-importer/oxt-wrapper.js b/lib/remote-importer/oxt-wrapper.js index f1c482e..894e59d 100644 --- a/lib/remote-importer/oxt-wrapper.js +++ b/lib/remote-importer/oxt-wrapper.js @@ -20,7 +20,7 @@ class OxtWrapper extends Wrapper { * Constructor */ constructor(url) { - super(url, keys.indexer.socks5Proxy) + super(url, keys.indexer.socks5Proxy) } /** @@ -55,7 +55,7 @@ class OxtWrapper extends Wrapper { // Try to retrieve more txs than the 1000 managed by the backend const uri = `/addresses/${address}/txids?count=${keys.addrFilterThreshold + 1}` const result = await this._get(uri) - + const ret = { address: address, ntx: result.count, @@ -109,6 +109,19 @@ class OxtWrapper extends Wrapper { return ret } + /** + * Retrieve the height of the chaintip for the remote source + * @returns {Promise} returns an object + * {chainTipHeight: } + */ + async getChainTipHeight() { + let chainTipHeight = null + const result = await this._get(`/lastblock`) + if (result != null && result['data'].length == 1) + chainTipHeight = parseInt(result['data'][0]['height']) + return {'chainTipHeight': chainTipHeight} + } + } module.exports = OxtWrapper diff --git a/lib/remote-importer/remote-importer.js b/lib/remote-importer/remote-importer.js index 3c4be3b..54b5e8c 100644 --- a/lib/remote-importer/remote-importer.js +++ b/lib/remote-importer/remote-importer.js @@ -25,7 +25,7 @@ if (network.key == 'bitcoin') { /** - * A singleton providing tools + * A singleton providing tools * for importing HD and loose addresses from remote sources */ class RemoteImporter { @@ -81,7 +81,7 @@ class RemoteImporter { if (!txParents[txid]) txParents[txid] = [] - + for (let i in tx.inputs) { const input = tx.inputs[i] let prev = input.outpoint.txid @@ -109,13 +109,13 @@ class RemoteImporter { * Import a list of transactions associated to a list of addresses * @param {object[]} addresses - array of addresses objects * @param {object[]} txns - array of transaction objects - * @returns {Promise} + * @returns {Promise} */ async _importTransactions(addresses, txns) { const addrIdMap = await db.getAddressesIds(addresses) - // The transactions array must be topologically ordered, such that - // entries earlier in the array MUST NOT depend upon any entry later + // The transactions array must be topologically ordered, such that + // entries earlier in the array MUST NOT depend upon any entry later // in the array. const txMaps = this._processTxsRelations(txns) const txOrdered = util.topologicalOrdering(txMaps.txParents, txMaps.txChildren) @@ -161,7 +161,7 @@ class RemoteImporter { if (gapLimit && ((keys.indexer.active == 'local_bitcoind') || (keys.indexer.active == 'local_indexer')) - ) { + ) { gaps = [gapLimit, gapLimit] } @@ -212,12 +212,12 @@ class RemoteImporter { * 4. Set u = highest chain index of used address, go to 1 * 5. Store all in database * - * @returns {object} returns + * @returns {object} returns * { * addresses: [{address, chain, index}], * transactions: [{ - * txid, - * version, + * txid, + * version, * locktime, * created, // if known * block: 'abcdef', // if confirmed @@ -331,7 +331,7 @@ class RemoteImporter { return true Logger.info(`Importer : Importing ${addresses.join(',')}`) - + try { const scanTx = [] const results = await this.sources.getAddresses(addresses, filterAddr) @@ -373,7 +373,7 @@ class RemoteImporter { if (N > 0) Logger.info(`Importer : Imported ${N} addresses in ${ts}s (${(dt/N).toFixed(0)} ms/addr)`) - + for (let address of addresses) delete this.importing[address] @@ -405,7 +405,7 @@ class RemoteImporter { const filteredTxs = txs.filter(tx => (tx.block && tx.block.hash == block.blockHash)) if (filteredTxs.length > 0) { const txids = filteredTxs.map(tx => tx.txid) - // Asynchronous confirmations + // Asynchronous confirmations db.confirmTransactions(txids, block.blockID) } } @@ -430,12 +430,12 @@ class RemoteImporter { } } await db.addOutputs(outputs) - + // Store the inputs in db const inputs = [] const spent = {} - // Get any outputs spent by the inputs of this transaction, + // Get any outputs spent by the inputs of this transaction, // add those database outIDs to the corresponding inputs, and store. let outpoints = [] for (let tx of txs) @@ -465,6 +465,15 @@ class RemoteImporter { } } + /** + * Retrieve the height of the chaintip for the remote source + * @returns {Promise} returns an object + * {chainTipHeight: } + */ + async getChainTipHeight() { + return this.sources.getChainTipHeight() + } + } module.exports = new RemoteImporter() diff --git a/lib/remote-importer/sources.js b/lib/remote-importer/sources.js index 8e8cef4..ee566cc 100644 --- a/lib/remote-importer/sources.js +++ b/lib/remote-importer/sources.js @@ -38,7 +38,7 @@ class Sources { try { const result = await this.source.getAddress(address, filterAddr) - + if (result.ntx) ret.ntx = result.ntx else if (result.txids) @@ -81,6 +81,22 @@ class Sources { } } + /** + * Retrieve the height of the chaintip + * @returns {Promise} returns an object + * {chainTipHeight: } + */ + async getChainTipHeight() { + let ret = {'chainTipHeight': null} + try { + ret = await this.source.getChainTipHeight() + } catch(e) { + Logger.error(e, `Importer : Sources.getChainTipHeight() : Error while retrieving the chaintip`) + } finally { + return ret + } + } + } module.exports = Sources From 2c9113bb8cc5b3e06c687f7aae51659817eba1ff Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 8 Oct 2020 14:25:03 +0200 Subject: [PATCH 13/54] add indexer info to /status endpoint --- accounts/status.js | 38 +++++++++++++++++++++++++++--- lib/remote-importer/oxt-wrapper.js | 5 +++- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/accounts/status.js b/accounts/status.js index 5c5fffa..f07e0b0 100644 --- a/accounts/status.js +++ b/accounts/status.js @@ -4,8 +4,12 @@ */ 'use strict' +const network = require('../lib/bitcoin/network') +const keys = require('../keys')[network.key] const util = require('../lib/util') +const Logger = require('../lib/logger') const db = require('../lib/db/mysql-db-wrapper') +const remote = require('../lib/remote-importer/remote-importer') /** @@ -32,8 +36,31 @@ class Status { const memory = `${util.toMb(process.memoryUsage().rss)} MiB` // Get highest block processed by the tracker - const highest = await db.getHighestBlock() - const dbMaxHeight = highest.blockHeight + let dbMaxHeight = null + try { + const highest = await db.getHighestBlock() + dbMaxHeight = highest.blockHeight + } catch(e) { + Logger.error(e, 'API : Status.getCurrent() :') + } + + // Get info about the indexer + const indexerType = keys.indexer.active + let indexerMaxHeight = null + let indexerUrl = null + + if (indexerType == 'third_party_explorer') { + indexerUrl = (network.key == 'bitcoin') + ? keys.indexer.oxt + : keys.indexer.esplora + } + + try { + const chaintip = await remote.getChainTipHeight() + indexerMaxHeight = chaintip['chainTipHeight'] + } catch(e) { + Logger.error(e, 'API : Status.getCurrent() :') + } return { uptime: uptime, @@ -43,7 +70,12 @@ class Status { sessions: this.sessions, max: this.maxConn }, - blocks: dbMaxHeight + blocks: dbMaxHeight, + indexer: { + type: indexerType, + url: indexerUrl, + maxHeight: indexerMaxHeight + } } } diff --git a/lib/remote-importer/oxt-wrapper.js b/lib/remote-importer/oxt-wrapper.js index 894e59d..291f3b6 100644 --- a/lib/remote-importer/oxt-wrapper.js +++ b/lib/remote-importer/oxt-wrapper.js @@ -33,7 +33,10 @@ class OxtWrapper extends Wrapper { url: `${this.base}${route}`, method: 'GET', json: true, - timeout: 15000 + timeout: 15000, + headers: { + 'User-Agent': 'Dojo' + } } // Sets socks proxy agent if required From 9205ed1c5c321ed214f9d4b828fa1f69c43ada92 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 9 Oct 2020 20:39:13 +0200 Subject: [PATCH 14/54] add db and indexer blocks to status dashboard --- static/admin/css/style.css | 4 ++ static/admin/dmt/status/status.html | 47 +++++++++++++++++ static/admin/dmt/status/status.js | 82 +++++++++++++++++++++++------ 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/static/admin/css/style.css b/static/admin/css/style.css index 3526b53..50f2262 100644 --- a/static/admin/css/style.css +++ b/static/admin/css/style.css @@ -453,6 +453,10 @@ button { color: #76d776; } +#indexer-status { + padding-bottom: 12px; +} + /* PAGES - PAIRING */ #qr-label, #qr-explorer-label { diff --git a/static/admin/dmt/status/status.html b/static/admin/dmt/status/status.html index 0377afd..e2b1ea4 100644 --- a/static/admin/dmt/status/status.html +++ b/static/admin/dmt/status/status.html @@ -5,6 +5,7 @@
+
FULL NODE
@@ -41,8 +42,36 @@
+ +
+
INDEXER
+
+
+ + + + + + + + + + + + + + + + + +
Status
Latest block
Indexer type
URL
+
+
+
+
+
TRACKER
@@ -64,6 +93,23 @@
+
+
DOJO DB
+
+
+ + + + + + + + + +
Status
Latest block
+
+
+
WEB
@@ -84,6 +130,7 @@
+
diff --git a/static/admin/dmt/status/status.js b/static/admin/dmt/status/status.js index d9e5e78..ae2f0bb 100644 --- a/static/admin/dmt/status/status.js +++ b/static/admin/dmt/status/status.js @@ -13,31 +13,73 @@ const statusScript = { }, refreshApiStatus: function() { + // Set default values displayed + this.setStatusIndicator('#db-status-ind', 'idle') + this.setStatusIndicator('#tracker-status-ind', 'idle') + this.setStatusIndicator('#indexer-status-ind', 'idle') + $('#tracker-uptime').text('-') + $('#tracker-chaintip').text('-') + $('#db-chaintip').text('-') + $('#indexer-chaintip').text('-') + $('#indexer-type').text('-') + $('#indexer-url').text('-') + //lib_msg.displayMessage('Loading API status info...'); return lib_api.getApiStatus().then(apiStatus => { if (apiStatus) { - $('#tracker-status-ind').html('✓') - $('#tracker-status-ind').css('color', '#76d776') $('#tracker-uptime').text(apiStatus['uptime']) - $('#tracker-chaintip').text(apiStatus['blocks']) + + const blocks = apiStatus['blocks'] + if (blocks) { + $('#db-chaintip').text(blocks) + $('#tracker-chaintip').text(blocks) + this.setStatusIndicator('#db-status-ind', 'ok') + this.setStatusIndicator('#tracker-status-ind', 'ok') + } else { + this.setStatusIndicator('#db-status-ind', 'ko') + this.setStatusIndicator('#tracker-status-ind', 'ko') + } + + if (apiStatus['indexer']) { + const indexerMaxHeight = apiStatus['indexer']['maxHeight'] + if (indexerMaxHeight) { + $('#indexer-chaintip').text(indexerMaxHeight) + this.setStatusIndicator('#indexer-status-ind', 'ok') + } else { + this.setStatusIndicator('#indexer-status-ind', 'ko') + } + const indexerType = apiStatus['indexer']['type'] + if (indexerType) + $('#indexer-type').text(indexerType.replace(/_/g, ' ')) + const indexerUrl = apiStatus['indexer']['url'] + if (indexerUrl) + $('#indexer-url').text(indexerUrl) + } //lib_msg.cleanMessagesUi() } }).catch(e => { - $('#tracker-status-ind').text('X') - $('#tracker-status-ind').css('color', '#f77c7c') - $('#tracker-uptime').text('-') - $('#tracker-chaintip').text('-') + this.setStatusIndicator('#db-status-ind', 'ko') + this.setStatusIndicator('#tracker-status-ind', 'ko') + this.setStatusIndicator('#indexer-status-ind', 'ko') lib_errors.processError(e) }) }, refreshPushTxStatus: function() { + // Set default values displayed + this.setStatusIndicator('#node-status-ind', 'idle') + $('#node-uptime').text('-') + $('#node-chaintip').text('-') + $('#node-version').text('-') + $('#node-network').text('-') + $('#node-conn').text('-') + $('#node-relay-fee').text('-') + //lib_msg.displayMessage('Loading Tracker status info...'); lib_api.getPushtxStatus().then(pushTxStatus => { if (pushTxStatus) { const data = pushTxStatus['data'] - $('#node-status-ind').html('✓') - $('#node-status-ind').css('color', '#76d776') + this.setStatusIndicator('#node-status-ind', 'ok') const uptime = lib_cmn.timePeriod(data['uptime']) $('#node-uptime').text(uptime) $('#node-chaintip').text(data['bitcoind']['blocks']) @@ -49,18 +91,24 @@ const statusScript = { //lib_msg.cleanMessagesUi() } }).catch(e => { - $('#node-status-ind').text('-') - $('#node-status-ind').css('color', '#f77c7c') - $('#node-uptime').text('-') - $('#node-chaintip').text('-') - $('#node-version').text('-') - $('#node-network').text('-') - $('#node-conn').text('-') - $('#node-relay-fee').text('-') + this.setStatusIndicator('#node-status-ind', 'ko') lib_errors.processError(e) }) }, + setStatusIndicator: function(id, status) { + if (status == 'ok') { + $(id).html('✓') + $(id).css('color', '#76d776') + } else if (status == 'ko') { + $(id).html('X') + $(id).css('color', '#f77c7c') + } else { + $(id).html('-') + $(id).css('color', '#efefef') + } + }, + } screenScripts.set('#screen-status', statusScript) \ No newline at end of file From 50ba4d367a81126a90a98ed7b4b7273fde96bfea Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 9 Oct 2020 20:39:13 +0200 Subject: [PATCH 15/54] add visual indicator for desynchronized chaintips on status screen --- static/admin/dmt/status/status.js | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/static/admin/dmt/status/status.js b/static/admin/dmt/status/status.js index ae2f0bb..f427e6c 100644 --- a/static/admin/dmt/status/status.js +++ b/static/admin/dmt/status/status.js @@ -1,6 +1,9 @@ const statusScript = { initPage: function() { + this.chaintipBitcoind = 0 + this.chaintipIndexer = 0 + this.chaintipDb = 0 // Refresh API status setInterval(() => {this.refreshApiStatus()}, 60000) // Refresh PushTx status @@ -31,6 +34,8 @@ const statusScript = { const blocks = apiStatus['blocks'] if (blocks) { + this.chaintipBitcoind = blocks + this.chaintipDb = blocks $('#db-chaintip').text(blocks) $('#tracker-chaintip').text(blocks) this.setStatusIndicator('#db-status-ind', 'ok') @@ -43,6 +48,7 @@ const statusScript = { if (apiStatus['indexer']) { const indexerMaxHeight = apiStatus['indexer']['maxHeight'] if (indexerMaxHeight) { + this.chaintipIndexer = indexerMaxHeight $('#indexer-chaintip').text(indexerMaxHeight) this.setStatusIndicator('#indexer-status-ind', 'ok') } else { @@ -55,6 +61,8 @@ const statusScript = { if (indexerUrl) $('#indexer-url').text(indexerUrl) } + + this.checkChaintips() //lib_msg.cleanMessagesUi() } }).catch(e => { @@ -82,12 +90,14 @@ const statusScript = { this.setStatusIndicator('#node-status-ind', 'ok') const uptime = lib_cmn.timePeriod(data['uptime']) $('#node-uptime').text(uptime) + this.chaintipBitcoind = data['bitcoind']['blocks'] $('#node-chaintip').text(data['bitcoind']['blocks']) $('#node-version').text(data['bitcoind']['version']) const network = data['bitcoind']['testnet'] == true ? 'testnet' : 'mainnet' $('#node-network').text(network) $('#node-conn').text(data['bitcoind']['conn']) $('#node-relay-fee').text(data['bitcoind']['relayfee']) + this.checkChaintips() //lib_msg.cleanMessagesUi() } }).catch(e => { @@ -96,6 +106,20 @@ const statusScript = { }) }, + checkChaintips: function() { + if (this.chaintipBitcoind > this.chaintipDb) { + this.setStatusIndicator('#db-status-ind', 'desynchronized') + this.setStatusIndicator('#tracker-status-ind', 'desynchronized') + } + if (this.chaintipBitcoind > this.chaintipIndexer) { + this.setStatusIndicator('#indexer-status-ind', 'desynchronized') + } else if (this.chaintipBitcoind < this.chaintipIndexer) { + this.setStatusIndicator('#node-status-ind', 'desynchronized') + this.setStatusIndicator('#db-status-ind', 'desynchronized') + this.setStatusIndicator('#tracker-status-ind', 'desynchronized') + } + }, + setStatusIndicator: function(id, status) { if (status == 'ok') { $(id).html('✓') @@ -103,6 +127,9 @@ const statusScript = { } else if (status == 'ko') { $(id).html('X') $(id).css('color', '#f77c7c') + } else if (status == 'desynchronized') { + $(id).html('✓') + $(id).css('color', '#f0c649') } else { $(id).html('-') $(id).css('color', '#efefef') From 60a43fff8fdbee79034a40e55186b14a0c293eb6 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 9 Oct 2020 20:44:00 +0200 Subject: [PATCH 16/54] upgrade indexer to addrindexrs 0.4.0 --- docker/my-dojo/.env | 2 +- docker/my-dojo/indexer/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index eb58d5b..0023aa4 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -17,7 +17,7 @@ DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 DOJO_TOR_VERSION_TAG=1.6.0 DOJO_EXPLORER_VERSION_TAG=1.4.0 -DOJO_INDEXER_VERSION_TAG=1.1.0 +DOJO_INDEXER_VERSION_TAG=1.2.0 DOJO_WHIRLPOOL_VERSION_TAG=1.2.0 diff --git a/docker/my-dojo/indexer/Dockerfile b/docker/my-dojo/indexer/Dockerfile index b838a13..f267c72 100644 --- a/docker/my-dojo/indexer/Dockerfile +++ b/docker/my-dojo/indexer/Dockerfile @@ -1,7 +1,7 @@ FROM rust:1.42.0-slim-buster ENV INDEXER_HOME /home/indexer -ENV INDEXER_VERSION 0.3.0 +ENV INDEXER_VERSION 0.4.0 ENV INDEXER_URL https://code.samourai.io/dojo/addrindexrs.git RUN apt-get update && \ From d0d11063a4c98576151ecd0dc4072c8bbb61d828 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Sat, 10 Oct 2020 18:31:13 +0200 Subject: [PATCH 17/54] replace request-promise-native by axios --- lib/remote-importer/esplora-wrapper.js | 18 +- lib/remote-importer/oxt-wrapper.js | 13 +- package-lock.json | 324 +------------------------ package.json | 3 +- 4 files changed, 32 insertions(+), 326 deletions(-) diff --git a/lib/remote-importer/esplora-wrapper.js b/lib/remote-importer/esplora-wrapper.js index 98e4b71..1a82039 100644 --- a/lib/remote-importer/esplora-wrapper.js +++ b/lib/remote-importer/esplora-wrapper.js @@ -4,7 +4,7 @@ */ 'use strict' -const rp = require('request-promise-native') +const axios = require('axios') const addrHelper = require('../bitcoin/addresses-helper') const util = require('../util') const Logger = require('../logger') @@ -34,15 +34,21 @@ class EsploraWrapper extends Wrapper { const params = { url: `${this.base}${route}`, method: 'GET', - json: true, - timeout: 15000 + responseType: 'json', + timeout: 15000, + headers: { + 'User-Agent': 'Dojo' + } } // Sets socks proxy agent if required - if (keys.indexer.socks5Proxy != null) - params['agent'] = this.socksProxyAgent + if (keys.indexer.socks5Proxy != null) { + params['httpAgent'] = this.socksProxyAgent + params['httpsAgent'] = this.socksProxyAgent + } - return rp(params) + const result = await axios(params) + return result.data } /** diff --git a/lib/remote-importer/oxt-wrapper.js b/lib/remote-importer/oxt-wrapper.js index 291f3b6..6bc66fa 100644 --- a/lib/remote-importer/oxt-wrapper.js +++ b/lib/remote-importer/oxt-wrapper.js @@ -4,7 +4,7 @@ */ 'use strict' -const rp = require('request-promise-native') +const axios = require('axios') const Logger = require('../logger') const network = require('../bitcoin/network') const keys = require('../../keys')[network.key] @@ -32,7 +32,7 @@ class OxtWrapper extends Wrapper { const params = { url: `${this.base}${route}`, method: 'GET', - json: true, + responseType: 'json', timeout: 15000, headers: { 'User-Agent': 'Dojo' @@ -40,10 +40,13 @@ class OxtWrapper extends Wrapper { } // Sets socks proxy agent if required - if (keys.indexer.socks5Proxy != null) - params['agent'] = this.socksProxyAgent + if (keys.indexer.socks5Proxy != null) { + params['httpAgent'] = this.socksProxyAgent + params['httpsAgent'] = this.socksProxyAgent + } - return rp(params) + const result = await axios(params) + return result.data } /** diff --git a/package-lock.json b/package-lock.json index aa42406..a7451db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,17 +26,6 @@ "es6-promisify": "5.0.0" } }, - "ajv": { - "version": "6.12.3", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz", - "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==", - "requires": { - "fast-deep-equal": "3.1.3", - "fast-json-stable-stringify": "2.1.0", - "json-schema-traverse": "0.4.1", - "uri-js": "4.2.2" - } - }, "ansi-colors": { "version": "3.2.3", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", @@ -124,19 +113,6 @@ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { - "safer-buffer": "2.1.2" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, "async": { "version": "1.5.2", "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", @@ -150,20 +126,13 @@ "double-ended-queue": "2.1.0-0" } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==" + "axios": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.20.0.tgz", + "integrity": "sha512-ANA4rr2BDcmmAQLOKft2fufrtuvlqR+cXNNinUmvfeSNCOF98PZL+7M/v1zIdGo7OLjEA9J2gXJL+j4zGsl0bA==", + "requires": { + "follow-redirects": "1.13.0" + } }, "babel-runtime": { "version": "5.8.38", @@ -187,14 +156,6 @@ "safe-buffer": "5.2.1" } }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { - "tweetnacl": "0.14.5" - } - }, "bech32": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", @@ -517,11 +478,6 @@ "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.0.tgz", "integrity": "sha1-FkpUg+Yw+kMh5a8HAg5TGDGyYJs=" }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" - }, "chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", @@ -639,14 +595,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { - "delayed-stream": "1.0.0" - } - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -718,14 +666,6 @@ "sha.js": "2.4.11" } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" - } - }, "dasherize": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dasherize/-/dasherize-2.0.0.tgz", @@ -767,11 +707,6 @@ "object-keys": "1.1.1" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" - }, "delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", @@ -818,15 +753,6 @@ "create-hmac": "1.1.7" } }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { - "jsbn": "0.1.1", - "safer-buffer": "2.1.2" - } - }, "ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -1091,26 +1017,6 @@ "resolved": "https://registry.npmjs.org/express-unless/-/express-unless-0.3.1.tgz", "integrity": "sha1-JVfBRudb65A+LSR/m1ugFFJpbiA=" }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" - }, "feature-policy": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz", @@ -1169,20 +1075,10 @@ "is-buffer": "2.0.4" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.8", - "mime-types": "2.1.27" - } + "follow-redirects": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz", + "integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA==" }, "forwarded": { "version": "0.1.2", @@ -1244,14 +1140,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - } - }, "github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", @@ -1286,20 +1174,6 @@ "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "6.12.3", - "har-schema": "2.0.0" - } - }, "has": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", @@ -1445,16 +1319,6 @@ } } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.16.1" - } - }, "iconv-lite": { "version": "0.4.23", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz", @@ -1583,11 +1447,6 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" - }, "js-yaml": { "version": "3.13.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", @@ -1598,26 +1457,6 @@ "esprima": "4.0.1" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" - }, "jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", @@ -1642,17 +1481,6 @@ } } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, "jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", @@ -1999,11 +1827,6 @@ "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -2150,11 +1973,6 @@ "sha.js": "2.4.11" } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, "picomatch": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", @@ -2216,11 +2034,6 @@ "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" - }, "pump": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", @@ -2230,11 +2043,6 @@ "once": "1.4.0" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" - }, "pushdata-bitcoin": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/pushdata-bitcoin/-/pushdata-bitcoin-1.0.1.tgz", @@ -2307,51 +2115,6 @@ "resolved": "https://registry.npmjs.org/referrer-policy/-/referrer-policy-1.2.0.tgz", "integrity": "sha512-LgQJIuS6nAy1Jd88DCQRemyE3mS+ispwlqMk3b0yjZ257fI1v9c+/p6SD5gP5FGyXUIgrNOAfmyioHwZtYv2VA==" }, - "request": { - "version": "2.88.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", - "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.10.0", - "caseless": "0.12.0", - "combined-stream": "1.0.8", - "extend": "3.0.2", - "forever-agent": "0.6.1", - "form-data": "2.3.3", - "har-validator": "5.1.3", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.27", - "oauth-sign": "0.9.0", - "performance-now": "2.1.0", - "qs": "6.5.2", - "safe-buffer": "5.2.1", - "tough-cookie": "2.4.3", - "tunnel-agent": "0.6.0", - "uuid": "3.4.0" - } - }, - "request-promise-core": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", - "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "requires": { - "lodash": "4.17.19" - } - }, - "request-promise-native": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", - "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", - "requires": { - "request-promise-core": "1.1.1", - "stealthy-require": "1.1.1", - "tough-cookie": "2.4.3" - } - }, "require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", @@ -2514,32 +2277,11 @@ "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.1.tgz", "integrity": "sha1-R1OT/56RR5rqYtyvDKPRSYOn+0A=" }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { - "asn1": "0.2.4", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.2", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.2", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "safer-buffer": "2.1.2", - "tweetnacl": "0.14.5" - } - }, "statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" - }, "string-width": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", @@ -2691,22 +2433,6 @@ "is-number": "7.0.0" } }, - "tough-cookie": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", - "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", - "requires": { - "psl": "1.8.0", - "punycode": "1.4.1" - }, - "dependencies": { - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - } - } - }, "tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", @@ -2715,11 +2441,6 @@ "safe-buffer": "5.2.1" } }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, "type-is": { "version": "1.6.18", "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", @@ -2752,14 +2473,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { - "punycode": "2.1.1" - } - }, "util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -2770,11 +2483,6 @@ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" - }, "validator": { "version": "10.8.0", "resolved": "https://registry.npmjs.org/validator/-/validator-10.8.0.tgz", @@ -2793,16 +2501,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, "websocket": { "version": "1.0.28", "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.28.tgz", diff --git a/package.json b/package.json index e05353f..4f6aa28 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "homepage": "https://code.samourai.io/dojo/samourai-dojo", "dependencies": { "async-sema": "2.1.2", + "axios": "0.20.0", "bip39": "2.4.0", "bitcoind-rpc-client": "0.3.1", "bitcoinjs-lib": "5.1.4", @@ -30,8 +31,6 @@ "mysql": "2.16.0", "passport": "0.4.0", "passport-localapikey-update": "0.6.0", - "request": "2.88.0", - "request-promise-native": "1.0.5", "socks-proxy-agent": "4.0.1", "validator": "10.8.0", "websocket": "1.0.28", From a2b65b134ac9f53dd097ab9debb825d4c551b988 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Wed, 18 Nov 2020 14:33:29 +0100 Subject: [PATCH 18/54] upgrade whirlpool to whirlpool-cli 0.10.9 --- docker/my-dojo/.env | 2 +- docker/my-dojo/whirlpool/Dockerfile | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index 0023aa4..8d0296e 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -18,7 +18,7 @@ DOJO_NGINX_VERSION_TAG=1.5.0 DOJO_TOR_VERSION_TAG=1.6.0 DOJO_EXPLORER_VERSION_TAG=1.4.0 DOJO_INDEXER_VERSION_TAG=1.2.0 -DOJO_WHIRLPOOL_VERSION_TAG=1.2.0 +DOJO_WHIRLPOOL_VERSION_TAG=1.3.0 ######################################### diff --git a/docker/my-dojo/whirlpool/Dockerfile b/docker/my-dojo/whirlpool/Dockerfile index 71b35df..396c2c9 100644 --- a/docker/my-dojo/whirlpool/Dockerfile +++ b/docker/my-dojo/whirlpool/Dockerfile @@ -60,10 +60,10 @@ RUN set -ex && \ # Install whirlpool-cli ENV WHIRLPOOL_URL https://code.samourai.io/whirlpool/whirlpool-client-cli/uploads -ENV WHIRLPOOL_VERSION 0.10.8 -ENV WHIRLPOOL_VERSION_HASH 7998ea5a9bb180451616809bc346b9ac +ENV WHIRLPOOL_VERSION 0.10.9 +ENV WHIRLPOOL_VERSION_HASH 602666c59f95ce72f1466f72d9c853e3 ENV WHIRLPOOL_JAR "whirlpool-client-cli-$WHIRLPOOL_VERSION-run.jar" -ENV WHIRLPOOL_SHA256 62e17b6020d0821a98e99ebb773b46191770ec186ceaa3e616a428f5cafe9f49 +ENV WHIRLPOOL_SHA256 9de3ceaff6e8cc0849bde58bc9e17b9c602352df8659adc67ab95b39cf046e4c RUN set -ex && \ cd "$WHIRLPOOL_DIR" && \ @@ -81,7 +81,7 @@ RUN chown whirlpool:whirlpool /restart.sh && \ chmod u+x /restart.sh && \ chmod g+x /restart.sh -# Expose HTTP API port +# Expose HTTP API port EXPOSE 8898 # Switch to user whirlpool From 63a4af54a6ba450695cb923b97d22e2022ef6e9e Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 19 Nov 2020 13:04:44 +0100 Subject: [PATCH 19/54] deactivate liquidity client (will be activated with next version) --- docker/my-dojo/whirlpool/restart.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/docker/my-dojo/whirlpool/restart.sh b/docker/my-dojo/whirlpool/restart.sh index ee0469e..6c69eea 100644 --- a/docker/my-dojo/whirlpool/restart.sh +++ b/docker/my-dojo/whirlpool/restart.sh @@ -12,6 +12,7 @@ whirlpool_options=( --cli.torConfig.coordinator.onion=true --cli.torConfig.backend.enabled=false --cli.torConfig.backend.onion=false + --cli.mix.liquidityClient=false ) if [ "$COMMON_BTC_NETWORK" == "testnet" ]; then From b367f42814326d888787e0c2d349d82e20d74bca Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 19 Nov 2020 17:10:04 +0100 Subject: [PATCH 20/54] track progress of import/rescan --- accounts/xpub-rest-api.js | 25 ++++++++++++++++++------- doc/GET_xpub_import_status.md | 11 +++++++++++ lib/remote-importer/remote-importer.js | 23 +++++++++++++++++++---- 3 files changed, 48 insertions(+), 11 deletions(-) diff --git a/accounts/xpub-rest-api.js b/accounts/xpub-rest-api.js index b612a5b..cce81d8 100644 --- a/accounts/xpub-rest-api.js +++ b/accounts/xpub-rest-api.js @@ -16,6 +16,7 @@ const RpcClient = require('../lib/bitcoind-rpc/rpc-client') const HdAccountInfo = require('../lib/wallet/hd-account-info') const authMgr = require('../lib/auth/authorizations-manager') const HttpServer = require('../lib/http-server/http-server') +const remoteImporter = require('../lib/remote-importer/remote-importer') const debugApi = !!(process.argv.indexOf('api-debug') > -1) const gap = require('../keys/')[network.key].gap @@ -230,8 +231,18 @@ class XPubRestApi { return HttpServer.sendError(res, e) } - const ret = { - import_in_progress: hdaService.importInProgress(xpub) + let ret = { + import_in_progress: false + } + + const status = hdaService.importInProgress(xpub) + if (status != null) { + ret['import_in_progress'] = true + ret['status'] = status['status'] + if (ret['status'] == remoteImporter.STATUS_RESCAN) + ret['hits'] = status['txs_int'] + status['txs_ext'] + else + ret['hits'] = status['txs'] } HttpServer.sendOkData(res, ret) @@ -275,7 +286,7 @@ class XPubRestApi { const argAddr = req.body.address const argSig = req.body.signature const argMsg = req.body.message - + // Translate xpub if needed try { const ret = this.xlatHdAccount(argXpub) @@ -323,7 +334,7 @@ class XPubRestApi { const argXpub = req.params.xpub const argAddr = req.body.address const argSig = req.body.signature - + // Translate xpub if needed try { const ret = this.xlatHdAccount(argXpub) @@ -398,15 +409,15 @@ class XPubRestApi { validateArgsPostXpub(req, res, next) { const isValidXpub = validator.isAlphanumeric(req.body.xpub) - const isValidSegwit = + const isValidSegwit = !req.body.segwit || validator.isAlphanumeric(req.body.segwit) - const isValidType = + const isValidType = !req.body.type || validator.isAlphanumeric(req.body.type) - const isValidForce = + const isValidForce = !req.body.force || validator.isAlphanumeric(req.body.force) diff --git a/doc/GET_xpub_import_status.md b/doc/GET_xpub_import_status.md index 4c9849a..8d0d45e 100644 --- a/doc/GET_xpub_import_status.md +++ b/doc/GET_xpub_import_status.md @@ -27,6 +27,17 @@ Status code 200 with JSON response: } ``` +```json +{ + "status": "ok", + "data": { + "import_in_progress": true, + "status": "rescan", + "hits": 1143 + } +} +``` + #### Failure Status code 400 with JSON response: ```json diff --git a/lib/remote-importer/remote-importer.js b/lib/remote-importer/remote-importer.js index 54b5e8c..a46aa7e 100644 --- a/lib/remote-importer/remote-importer.js +++ b/lib/remote-importer/remote-importer.js @@ -34,6 +34,8 @@ class RemoteImporter { * Constructor */ constructor() { + this.STATUS_RESCAN = 'rescan' + this.STATUS_IMPORT = 'import' // Guard against overlapping imports this.importing = {} this.sources = new Sources() @@ -50,12 +52,12 @@ class RemoteImporter { /** * Check if a xpub is currently being imported or rescanned by Dojo - * Returns true if import/rescan is in progress, otherwise returns false + * Returns infor about the operation if import/rescan is in progress, otherwise returns null * @param {string} xpub - xpub - * @returns {boolean} + * @returns {object} */ importInProgress(xpub) { - return this.importing[xpub] ? true : false + return this.importing[xpub] ? this.importing[xpub] : null } /** @@ -147,7 +149,11 @@ class RemoteImporter { return Promise.reject(errors.xpub.OVERLAP) } - this.importing[xpub] = true + this.importing[xpub] = { + 'status': this.STATUS_RESCAN, + 'txs_ext': 0, + 'txs_int': 0 + } const ts = hdaHelper.typeString(type) Logger.info(`Importer : Importing ${xpub} ${ts}`) @@ -182,6 +188,11 @@ class RemoteImporter { addresses = addresses.concat(result.addresses) } + this.importing[xpub] = { + 'status': this.STATUS_IMPORT, + 'txs': txns.length + } + // Store the hdaccount and the addresses into the database await db.ensureHDAccountId(xpub, type) await db.addAddressesToHDAccount(xpub, addresses) @@ -290,6 +301,10 @@ class RemoteImporter { } if (gotTransactions) { + if (c == 0) + this.importing[xpub]['txs_ext'] = Object.keys(txids).length + else + this.importing[xpub]['txs_int'] = Object.keys(txids).length // We must go deeper const result = await this.xpubScan(xpub, c, d, u, G, type, txids) // Accumulate results from further down the rabbit hole From 326b042e988daeee76898230b73ac77d00510e07 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 19 Nov 2020 17:10:36 +0100 Subject: [PATCH 21/54] display progress of import/rescan in DMT --- static/admin/dmt/xpubs-tools/xpubs-tools.js | 58 ++++++++------------- 1 file changed, 23 insertions(+), 35 deletions(-) diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.js b/static/admin/dmt/xpubs-tools/xpubs-tools.js index 7f02d1c..930663e 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.js +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.js @@ -94,25 +94,17 @@ const screenXpubsToolsScript = { jsonData['segwit'] = 'bip84' } - return lib_api.postXpub(jsonData) - .then(result => { - // Successful import + try { + lib_api.postXpub(jsonData) + // Wait for import completion and display progress + this.checkRescanStatus(() => { this._searchXpub(this.currentXpub).then(() => { lib_msg.displayInfo('Import complete') }) - }).catch(e => { - // Check if import has timeout'd or if we have an error - if (e['status'] == 502 || e['status'] == 504) { - // Wait for import completion - this.checkRescanStatus(() => { - this._searchXpub(this.currentXpub).then(() => { - lib_msg.displayInfo('Import complete') - }) - }) - } else { - lib_errors.processError(e) - } }) + } catch(e) { + lib_errors.processError(e) + } }, rescanXpub: function() { @@ -121,35 +113,31 @@ const screenXpubsToolsScript = { startIdx = (startIdx == null) ? 0 : parseInt(startIdx) let lookahead = $('#rescan-lookahead').val() lookahead = (lookahead == null) ? 100 : parseInt(lookahead) - return lib_api.getXpubRescan(this.currentXpub, lookahead, startIdx) - .then(() => { - // Successful rescan + + try { + lib_api.getXpubRescan(this.currentXpub, lookahead, startIdx) + // Wait for rescan completion and display progress + this.checkRescanStatus(() => { this.hideRescanForm() this._searchXpub(this.currentXpub).then(() => { lib_msg.displayInfo('Rescan complete') }) - }).catch(e => { - // Check if rescan has timeout'd or if we have an error - if (e['status'] == 502 || e['status'] == 504) { - // Wait for rescan completion - this.checkRescanStatus(() => { - this.hideRescanForm() - this._searchXpub(this.currentXpub).then(() => { - lib_msg.displayInfo('Rescan complete') - }) - }) - } else { - lib_errors.processError(e) - } }) + } catch(e) { + lib_errors.processError(e) + } }, checkRescanStatus: function(callback) { this.rescanStatusTimerId = setTimeout(() => { lib_api.getXpubRescanStatus(this.currentXpub) .then(result => { - if (result['data']['import_in_progress']) { - lib_msg.displayMessage('Rescan in progress. Please wait...'); + const data = result['data'] + if (data['import_in_progress']) { + const lblOp = (data['status'] == 'rescan') ? 'Rescan' : 'Import' + const lblHits = (data['status'] == 'rescan') ? 'hits detected' : 'transactions imported' + const msg = `${lblOp} in progress (${data['hits']} ${lblHits})` + lib_msg.displayMessage(msg) return this.checkRescanStatus(callback) } else { clearTimeout(this.rescanStatusTimerId) @@ -157,10 +145,10 @@ const screenXpubsToolsScript = { } }).catch(e => { lib_errors.processError(e) - lib_msg.displayMessage('Rescan in progress. Please wait...'); + lib_msg.displayMessage('Rescan in progress. Please wait...') return this.checkRescanStatus(callback) }) - }, 10000) + }, 1000) }, setXpubDetails: function(xpubInfo) { From c71b3064bba08a16b29d6f6ba238da52092d6993 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 19 Nov 2020 17:29:09 +0100 Subject: [PATCH 22/54] check that jqxhr['responseJSON']['error'] is a string --- static/admin/lib/errors-utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/static/admin/lib/errors-utils.js b/static/admin/lib/errors-utils.js index e32c528..106cd16 100644 --- a/static/admin/lib/errors-utils.js +++ b/static/admin/lib/errors-utils.js @@ -4,7 +4,8 @@ const lib_errors = { extractJqxhrErrorMsg: function(jqxhr) { let hasErrorMsg = ('responseJSON' in jqxhr) && (jqxhr['responseJSON'] != null) && - ('error' in jqxhr['responseJSON']) + ('error' in jqxhr['responseJSON']) && + (typeof jqxhr['responseJSON']['error'] == 'string') return hasErrorMsg ? jqxhr['responseJSON']['error'] : jqxhr.statusText }, From be4a345f17a43d12d0c83183b157ebc1df935167 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 4 Dec 2020 19:36:55 +0100 Subject: [PATCH 23/54] prevent restart of bitcoin container if bitcoind fails --- docker/my-dojo/bitcoin/restart.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/bitcoin/restart.sh b/docker/my-dojo/bitcoin/restart.sh index d8cbf98..f4efa2d 100644 --- a/docker/my-dojo/bitcoin/restart.sh +++ b/docker/my-dojo/bitcoin/restart.sh @@ -44,7 +44,7 @@ if [ "$COMMON_BTC_NETWORK" == "testnet" ]; then bitcoind_options+=(-testnet) fi -bitcoind "${bitcoind_options[@]}" +bitcoind "${bitcoind_options[@]}" || true # Keep the container up while true From 7f2e2c9f85b53e64784d0264c106b219e56eb672 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 4 Dec 2020 19:37:10 +0100 Subject: [PATCH 24/54] bump versio of bitcoin container --- docker/my-dojo/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index 8d0296e..555cb45 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -12,7 +12,7 @@ COMPOSE_CONVERT_WINDOWS_PATHS=1 DOJO_VERSION_TAG=1.9.0 DOJO_DB_VERSION_TAG=1.2.0 -DOJO_BITCOIND_VERSION_TAG=1.9.0 +DOJO_BITCOIND_VERSION_TAG=1.10.0 DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 DOJO_TOR_VERSION_TAG=1.6.0 From 6f6a3df4771e7a77eb76a0672ca981b161dbdfb8 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Mon, 7 Dec 2020 10:47:36 +0000 Subject: [PATCH 25/54] improve dojo shell script --- docker/my-dojo/dojo.sh | 77 ++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 33 deletions(-) diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index ff39c7a..96efbf7 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -55,7 +55,7 @@ select_yaml_files() { # Docker up docker_up() { yamlFiles=$(select_yaml_files) - eval "docker-compose $yamlFiles up $1 -d" + eval "docker-compose $yamlFiles up $@ -d" } # Start @@ -74,7 +74,7 @@ start() { # Stop stop() { echo "Preparing shutdown of Dojo. Please wait." - # Check if dojo is running (check the db container) + # Check if dojo is running (check the db container) isRunning=$(docker inspect --format="{{.State.Running}}" db 2> /dev/null) if [ $? -eq 1 ] || [ "$isRunning" == "false" ]; then echo "Dojo is already stopped." @@ -165,7 +165,7 @@ install() { if [ $launchInstall -eq 0 ]; then pastInstallsfound=$(docker image ls | grep samouraiwallet/dojo-db | wc -l) if [ $pastInstallsfound -ne 0 ]; then - # Past installation found. Ask confirmation forreinstall + # Past installation found. Ask confirmation for reinstall echo -e "\nWarning: Found traces of a previous installation of Dojo on this machine." echo "A new installation requires to remove these elements first." if [ $auto -eq 0 ]; then @@ -200,9 +200,16 @@ install() { init_config_files # Build and start Dojo docker_up --remove-orphans - # Display the logs - if [ $noLog -eq 1 ]; then - logs "" 0 + buildResult=$? + if [ $buildResult -eq 0 ]; then + # Display the logs + if [ $noLog -eq 1 ]; then + logs "" 0 + fi + else + # Return an error + echo -e "\nInstallation of Dojo failed. See the above error message." + exit $buildResult fi fi } @@ -233,20 +240,8 @@ uninstall() { fi if [ $launchUninstall -eq 0 ]; then - docker-compose rm -f - yamlFiles=$(select_yaml_files) - eval "docker-compose $yamlFiles down" - - docker image rm -f samouraiwallet/dojo-db:"$DOJO_DB_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-bitcoind:"$DOJO_BITCOIND_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-explorer:"$DOJO_EXPLORER_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-nodejs:"$DOJO_NODEJS_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-nginx:"$DOJO_NGINX_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-tor:"$DOJO_TOR_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-indexer:"$DOJO_INDEXER_VERSION_TAG" - docker image rm -f samouraiwallet/dojo-whirlpool:"$DOJO_WHIRLPOOL_VERSION_TAG" - + eval "docker-compose $yamlFiles down --rmi all" docker volume prune -f return 0 else @@ -260,13 +255,12 @@ del_images_for() { # $2: most recent version of the image (do not delete this one) docker image ls | grep "$1" | sed "s/ \+/,/g" | cut -d"," -f2 | while read -r version ; do if [ "$2" != "$version" ]; then - docker image rm "$1:$version" + docker image rm -f "$1:$version" fi done } clean() { - docker image prune del_images_for samouraiwallet/dojo-db "$DOJO_DB_VERSION_TAG" del_images_for samouraiwallet/dojo-bitcoind "$DOJO_BITCOIND_VERSION_TAG" del_images_for samouraiwallet/dojo-explorer "$DOJO_EXPLORER_VERSION_TAG" @@ -275,6 +269,7 @@ clean() { del_images_for samouraiwallet/dojo-tor "$DOJO_TOR_VERSION_TAG" del_images_for samouraiwallet/dojo-indexer "$DOJO_INDEXER_VERSION_TAG" del_images_for samouraiwallet/dojo-whirlpool "$DOJO_WHIRLPOOL_VERSION_TAG" + docker image prune -f } # Upgrade @@ -311,7 +306,16 @@ upgrade() { if [ $launchUpgrade -eq 0 ]; then # Select yaml files yamlFiles=$(select_yaml_files) + # Check if dojo is running (check the db container) + isRunning=$(docker inspect --format="{{.State.Running}}" db 2> /dev/null) + if [ $? -eq 1 ] || [ "$isRunning" == "false" ]; then + echo -e "\nChecked that Dojo isn't running." + else + echo " " + stop + fi # Update config files + echo -e "\nPreparing the upgrade of Dojo.\n" update_config_files # Cleanup cleanup @@ -320,19 +324,26 @@ upgrade() { export BITCOIND_RPC_EXTERNAL_IP # Rebuild the images (with or without cache) if [ $noCache -eq 0 ]; then - eval "docker-compose $yamlFiles build --no-cache" - else - eval "docker-compose $yamlFiles build" + echo -e "\nDeleting Dojo containers and images." + eval "docker-compose $yamlFiles down --rmi all" fi - # Start Dojo - docker_up --remove-orphans - # Post start clean-up - post_start_cleanup - # Update the database - update_dojo_db - # Display the logs - if [ $noLog -eq 1 ]; then - logs "" 0 + echo -e "\nStarting the upgrade of Dojo.\n" + docker_up --build --force-recreate --remove-orphans + buildResult=$? + if [ $buildResult -eq 0 ]; then + # Post start clean-up + clean + post_start_cleanup + # Update the database + update_dojo_db + # Display the logs + if [ $noLog -eq 1 ]; then + logs "" 0 + fi + else + # Return an error + echo -e "\nUpgrade of Dojo failed. See the above error message." + exit $buildResult fi fi } From 6ce6e633b0d056629878fd3cfb2f80c065f5bc0a Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Sat, 19 Dec 2020 17:05:26 +0100 Subject: [PATCH 26/54] update samourai logo --- static/admin/css/style.css | 2 +- static/admin/icons/samourai-logo-loading.png | Bin 17251 -> 0 bytes static/admin/icons/samourai-logo-trans@2x.png | Bin 10170 -> 0 bytes static/admin/icons/samourai-logo.png | Bin 0 -> 5717 bytes static/admin/index.html | 2 +- 5 files changed, 2 insertions(+), 2 deletions(-) delete mode 100644 static/admin/icons/samourai-logo-loading.png delete mode 100644 static/admin/icons/samourai-logo-trans@2x.png create mode 100644 static/admin/icons/samourai-logo.png diff --git a/static/admin/css/style.css b/static/admin/css/style.css index 50f2262..7ffc507 100644 --- a/static/admin/css/style.css +++ b/static/admin/css/style.css @@ -265,7 +265,7 @@ td.table-value { /* PAGES - COMMONS */ body.dmt { min-height: 100vh; - background-image: url("../icons/samourai-logo-loading.png"); + background-image: url("../icons/samourai-logo.png"); background-repeat: no-repeat; background-position: center; } diff --git a/static/admin/icons/samourai-logo-loading.png b/static/admin/icons/samourai-logo-loading.png deleted file mode 100644 index 8df79af3c0f8220a0db32bb0a7dc2972d7e106b3..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17251 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

;)#&09*|>uNBH;y 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 WF5LBIlmV{yWAJqKb6Mw<&;$U|iO){} diff --git a/static/admin/icons/samourai-logo-trans@2x.png b/static/admin/icons/samourai-logo-trans@2x.png deleted file mode 100644 index 50a599c6a64661e1d40df48c73b099e94de56f4d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10170 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

`-P? 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 KW1Fn-7ybj8C2kY| literal 0 HcmV?d00001 diff --git a/static/admin/index.html b/static/admin/index.html index 5073354..adb5ff0 100644 --- a/static/admin/index.html +++ b/static/admin/index.html @@ -23,7 +23,7 @@

- +

DOJO // MAINTENANCE TOOL beta

From 0c81a1b37d72765e52a6dbe33ccced8eeeb8b5a0 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Sat, 19 Dec 2020 18:27:15 +0000 Subject: [PATCH 27/54] add a detailed installation and upgrade guide for ubuntu --- doc/DOCKER_setup.md | 10 +- doc/DOCKER_ubuntu_setup.md | 509 +++++++++++++++++++++++++++++++++++++ 2 files changed, 515 insertions(+), 4 deletions(-) create mode 100644 doc/DOCKER_ubuntu_setup.md diff --git a/doc/DOCKER_setup.md b/doc/DOCKER_setup.md index 150d9aa..9217bc6 100644 --- a/doc/DOCKER_setup.md +++ b/doc/DOCKER_setup.md @@ -61,7 +61,7 @@ MyDojo is a set of Docker containers providing a full Samourai backend composed | | ---------- ---------- | | whirlnet | dojonet | |_________________|______________________________________________________________| - Host machine + Host machine @@ -109,6 +109,8 @@ Most options provided in the configuration files can be later modified. New valu ## First-time Setup ## +For Ubuntu 16, see this detailed [installation and upgrade guide](./DOCKER_ubuntu_setup.MD). + For MacOS, see this detailed [installation guide](./DOCKER_mac_setup.MD). For Synology, see this detailed [installation guide](./DOCKER_synology_setup.md). @@ -325,7 +327,7 @@ Once the database has finished syncing, you can pair your Samourai Wallet with y 1. Open the maintenance tool in a Tor browser (Tor v3 onion address) and sign in with your admin key. -2. Get your smartphone and launch the Samourai Wallet app. Scan the first QRCode displayed in the "Pairing" tab of the maintenance tool. +2. Get your smartphone and launch the Samourai Wallet app. Scan the first QRCode displayed in the "Pairing" tab of the maintenance tool. If you experience any problems when pairing, try re-installing the app and select "Connect to existing Dojo" from the [â‹®] menu. @@ -336,7 +338,7 @@ You can pair your Samourai Wallet with your local block explorer in 2 steps: 1. Open the maintenance tool in a Tor browser (Tor v3 onion address) and sign in with your admin key. -2. Get your smartphone and launch the Samourai Wallet app. Scan the second QRCode displayed in the "Pairing" tab of the maintenance tool. +2. Get your smartphone and launch the Samourai Wallet app. Scan the second QRCode displayed in the "Pairing" tab of the maintenance tool. @@ -353,7 +355,7 @@ The block explorer is accessed as a Tor hidden service (static onion address). The Whirlpool API is accessed as a Tor hidden service (static onion address). -The Whirlpool client connects to the Whirlpool Coordinator hidden service. +The Whirlpool client connects to the Whirlpool Coordinator hidden service. The Bitcoin node only allows incoming connections from Tor (ephemeral onion address). diff --git a/doc/DOCKER_ubuntu_setup.md b/doc/DOCKER_ubuntu_setup.md new file mode 100644 index 0000000..dc71d34 --- /dev/null +++ b/doc/DOCKER_ubuntu_setup.md @@ -0,0 +1,509 @@ +# Installation and Upgrade of Dojo on Ubuntu 16 + +This procedure is written for the installation of MyDojo on a host machine running Ubuntu 16 but it should be easy to adapt it for Linux distributions running on a x86-64 architecture. + + +## Table of Content ## + +- [Requirements](#requirements) +- [Storage locations](#storage) +- [Installation procedure](#install) +- [Upgrade procedure](#upgrade) + + + + +## 1/ Requirements ## + +The main requirements for the host machine running MyDojo are: + +* Processor: x86-64 +* OS: Linux 64 bits +* Disk Type: SSD (recommended) +* Disk Size: 600GB (min)/ 1TB (recommended) +* RAM: 2GB (min) / 4GB (recommended) + +__Additional Considerations__ + +* While MyDojo will work fine on a multipurpose computer, a dedicated host machine connected 24/7 to the network is recommended. MyDojo was primarily designed as a server always ready for use +* The clock of the host machine MUST be set properly (required for Tor) + + + + +## 2/ Storage Locations ## + +By default, MyDojo stores its code and data in 2 different locations: + +* __MyDojo application code__: source code of MyDojo + Dojo Shell Script + Configuration files + +* __MyDojo and Docker data__: persistent data required for a functional MyDojo (blockchain data, Dojo database, logs, Docker images) + +By default, MyDojo and Docker data will be stored under `/var/lib/docker` directory but the directory can be modified (e.g.: all data stored on an dedicated disk). + + + + +## 3/ First Installation + +The procedure described in this section will configure and install MyDojo with its own Indexer for fast rescans. See the [Advanced Setups](./DOCKER_advanced_setups.md) for more information about this module or for the additional installation of a Whirlpool client on your Dojo. + +### 3.1/ Prepare Host System + +First, we must prepare our host system for MyDojo by installing required operating system dependencies and configuring our linux system with privacy and security in mind. + +#### 3.1.1/ Install OS dependencies + +``` +> cd ~ +> sudo apt-get update +> sudo apt-get install apt-transport-https ca-certificates curl gnupg-agent software-properties-common unzip +``` + +#### 3.1.2/ Install Docker & Docker-Compose + +For an installation of Docker and Docker Compose with a different Linux distribution, please refer to the official [Docker](https://docs.docker.com/install/) and [Docker Compose](https://docs.docker.com/compose/install/) documentations. + +If Docker is already installed on the host machine remove old Docker versions installed on the computer + +``` +> sudo apt-get remove docker docker-engine docker.io containerd runc +``` + +__Download Docker's official PGP key__ + +``` +> curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - +> sudo apt-key fingerprint 0EBFCD88 +``` + +Verify that you now have the key with the fingerprint `9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88` + +__Install Docker__ + +``` +> sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" +> sudo apt-get update +> sudo apt-get install docker-ce docker-ce-cli containerd.io +``` + +__Test the installation of Docker__ + +``` +> sudo docker --version +``` + +This command should return the version of Docker if installation was successful. + + +__Install Docker Compose__ + +``` +> sudo curl -L "https://github.com/docker/compose/releases/download/1.25.3/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose +> sudo chmod +x /usr/local/bin/docker-compose +``` + +__Test the installation of Docker Compose__ + +``` +> sudo docker-compose --version +``` + +This command should return the version of Docker Compose if installation was successful. + +__Create a user account for MyDojo__ + +Creating a segregated user account for MyDojo is a good idea for security reasons. + + +#### 3.1.3/ Create a dojo user + +``` +> sudo useradd -s /bin/bash -d /home/dojo -m -G sudo dojo +> sudo passwd dojo +``` + +Enter and confirm the password for the `dojo` user. + +__Add the user to the docker group__ + +``` +> sudo usermod -aG docker dojo +``` + +__Restart Host System__ + +``` +> sudo shutdown -r now +``` + +Log back into the Host System with the `dojo` user and test the Docker installation + +``` +> docker run hello-world +``` + +This command should display a hello message if Docker can be run with the `dojo` user. + + +#### 3.1.4/ Configure Docker data storage directory (optional) + +This step should be applied if you don't want to store MyDojo and Docker data under the default `/var/lib/docker` directory (e.g.: all data will be stored on an external SSD). + +__Stop the Docker Service__ + +``` +> sudo systemctl stop docker +``` + +Create the directory that will store Docker data (replace `/path/to/target/directory/` by the correct path). + +``` +> sudo mkdir /path/to/target/directory/ +``` + +Temporarily switch to root and create the daemon.json file storing the path to your Docker direct (replace `/path/to/target/directory/` by the correct path). + +``` +> sudo su - root +> sudo echo '{ "data-root": "/path/to/target/directory/" }' > /etc/docker/daemon.json +> exit +``` + + +### 3.2/ Downloading MyDojo + +Now that the Host System has been prepared, we will download the latest version of MyDojo source code and configure it before proceeding with install. + + +#### 3.2.1/ Initialize the dojo app directory + +We first create a directory for housing our MyDojo files. In this guide we are naming this directory `dojo-app` and it will be located in the home directory of the `dojo` user. + +``` +> mkdir ~/dojo-app +``` + +#### 3.2.2/ Download latest MyDojo files + +Download and unpack the source archive for the latest version of MyDojo and copy these files to the newly created `dojo-app` directory with the following commands. + +``` +> cd ~ +> wget https://code.samourai.io/dojo/samourai-dojo/-/archive/master/samourai-dojo-master.zip +> unzip samourai-dojo-master.zip -d . +> cp -a samourai-dojo-master/. dojo-app/ +``` + +Delete the source archive now that we have copied the files to our directory. + +``` +> rm -rf samourai-dojo-master +> rm samourai-dojo-master.zip +``` + + +### 3.3/ Configuring MyDojo + +Change the working directory to the MyDojo configuration directory. + +``` +> cd ~/dojo-app/docker/my-dojo/conf +``` + +__Note:__ + +You will be required to generate various random alphanumeric passwords to secure various aspects of your Dojo installation. You can generate these in any way you wish, but you may wish to use the following command in a terminal session to generate these passwords with sufficient entropy: + +``` +> cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 64 | head -n 1 +``` + +#### 3.3.1/ Bitcoin configuration + +Edit the `docker-bitcoind.conf.tpl` file. + +``` +> nano docker-bitcoind.conf.tpl +``` + +Customize the content of the file + +``` +BITCOIND_RPC_USER= +BITCOIND_RPC_PASSWORD= +``` + +__Note:__ + +If your machine has a lot of RAM, it's recommended that you increase the value of `BITCOIND_DB_CACHE` (e.g 2048) for a faster Initial Block Download. + +Save and exit the file with `CTRL+X`, `Y` and `ENTER`. + + +#### 3.3.2/ Database configuration + +Edit the ``docker-mysql.conf.tpl`` file. + +``` +> nano docker-mysql.conf.tpl +``` + +Customize the content of the file + +``` +MYSQL_ROOT_PASSWORD= +MYSQL_USER= +MYSQL_PASSWORD= +``` + +Save and exit the file with `CTRL+X`, `Y` and `ENTER`. + + +#### 3.3.3/ NodeJS configuration + +Edit the `docker-node.conf.tpl` file. + +``` +> nano docker-node.conf.tpl +``` + +Customize the content of the file + +``` +NODE_API_KEY= +NODE_ADMIN_KEY= +NODE_JWT_SECRET= +NODE_ACTIVE_INDEXER=local_indexer +``` + +Save and exit the file with `CTRL+X`, `Y` and `ENTER`. + + +#### 3.3.4/ Indexer configuration + +Edit the `docker-indexer.conf.tpl` file. + +``` +> nano docker-indexer.conf.tpl +``` + +Customize the content of the file + +``` +INDEXER_INSTALL=on +``` + +Save and exit the file with `CTRL+X`, `Y` and `ENTER`. + + +#### 3.3.5/ Explorer configuration + +Edit the `docker-explorer.conf.tpl` file. + +``` +> nano docker-explorer.conf.tpl +``` + +Customize the content of the file + +``` +EXPLORER_KEY= +``` + +Save and exit the file with `CTRL+X`, `Y` and `ENTER`. + + +### 3.6/ Execute the first installation of MyDojo + +From this point on the install process is automatic. Launch the installation of MyDojo + +``` +> cd ~/dojo-app/docker/my-dojo +> ./dojo.sh install +``` + +Confirm the installation with `Y` and `ENTER`. + + +#### 3.6.1/ Understanding the Automatic Install + +At this point, your job is done. The Dojo Shell script and Docker are going to take over and a lot of things are going to happen automatically. + +__Building of MyDojo containers__ + +First, the shell script is going to initialize a set of configuration files. Then Docker is going to +initialize the dedicated networks and data volumes that will be used by MyDojo. At last, Docker is going +to build the Docker containers composing MyDojo. + +This latest operation may last from a dozen to a few tens of minutes, depending on the specs of the hardware. A lot of logs are displayed and you may be concerned that some of these logs (displayed in red) are the sign of a problem. Don't worry about this. Some of these logs are just informational or warnings. Just be aware that if a blocking error occurs, Docker will stop building the containers and you won't reach the next phase. + +__First launch of MyDojo__ + +If all Docker containers have been successfully built, the Shell Script is going to launch MyDojo (equivalent of the `dojo.sh start` command) and display its logs. + +Here you may be able to observe a few things in the logs: + +* The schema of the local database is initialized (done once during first installation) +* The Tor container is connecting to the Tor network +* Dojo application modules are started in the NodeJS container +* The Bitcoin daemon is launched, establishes connections to remote full nodes and starts to download block headers. +* The indexer is launched and waits for the completion of the Initial Blocks Download by the Bitcoin daemon. + +__Initial Blocks Download__ + +When all blocks headers have been successfully downloaded and processed, the Bitcoin daemon is going to process its Initial Blocks Download (IBD). In parallel, the `Tracker` module of Dojo is going to process its own IBD thanks to data provided by the Bitcoin daemon. + +This phase is going to last from 1 to several days, depending on the specs of the hardware. At this point, all you have to do is wait. The Bitcoin daemon is processing the IBD while the Tracker is importing block headers in parallel. + +Note: You can exit the logs with `CTRL+C`. Don't worry, MyDojo is still running in background. + +__Address Indexing__ + +When the Bitcoin daemon has completed its IBD, the Indexer will automatically start the indexation of all Bitcoin addresses. When the indexation is complete, it will compact the database. + +These operations should last a few more hours, depending on the specs of the hardware. + +Note: You may notice errors returned by the Block Explorer during all these operations. Don't worry. The Block Explorer should be available as soon as the Indexer has completed its tasks. + + +### 3.7/ Monitor Install Progress with the Maintenance Tool (DMT) + +Retrieve the onion address of the DMT with the commands + +``` +> cd ~/dojo-app/docker/my-dojo +> ./dojo.sh onion +``` + +Open the DMT with the Tor browser and check the `Status` page. + +If a green check is displayed for all modules and if the chaintip displayed for all modules match with the chaintip displayed by a third-party Block Explorer, then your Dojo is ready. + + + + + +## 4/ Upgrade + +The procedures described in this section will upgrade your Dojo to the most recent version or to a specific version of Dojo. + + +### 4.1/ Upgrade to latest version + +This procedure allows to upgrade MyDojo to the latest version. + +#### 4.1.1/ Stop MyDojo + +``` +> cd ~/dojo-app/docker/my-dojo +> ./dojo.sh stop +``` + +#### 4.1.2/ Update the code of MyDojo + +Download the archive of latest version + +``` +> cd ~ +> wget https://code.samourai.io/dojo/samourai-dojo/-/archive/master/samourai-dojo-master.zip +``` + +Uncompress the archive + +``` +> unzip samourai-dojo-master.zip -d . +``` + +Overwrite the dojo-app directory with the content of the archive + +``` +> cp -a samourai-dojo-master/. dojo-app/ +``` + +#### 4.1.3/ Update Configuration (optional) + +Check the [release notes](https://code.samourai.io/dojo/samourai-dojo/-/blob/master/RELEASES.md) of the new vesion for a list of new features that may require to tune the value of new configuration options. + +If applicable, edit the templates files stored in `~/dojo-app/docker/my-dojo/conf/` and modify the values set for new configuration options. + + +#### 4.1.4/ Start Upgrade + +``` +> cd ~/dojo-app/docker/my-dojo +> ./dojo.sh upgrade +``` + +Confirm that you want to upgrade MyDojo with `Y` and `ENTER`. + +The shell script is going to rebuild the Docker containers. MyDojo will be automatically restarted after the containers have been rebuilt. + + +#### 4.1.5/ Cleanup + +``` +> cd ~ +> rm -rf samourai-dojo-master +> rm samourai-dojo-master.zip +``` + + +### 4.2/ Upgrade to a specific version + +This procedure allows to upgrade MyDojo to a specific version `X.Y.Z` + +#### 4.2.1/ Stop MyDojo + +``` +> cd ~/dojo-app/docker/my-dojo +> ./dojo.sh stop +``` + +#### 4.2.2/ Update the code of MyDojo + +Download the archive of version `X.Y.Z` + +``` +> cd ~ +> wget https://code.samourai.io/dojo/samourai-dojo/-/archive/vX.Y.Z/samourai-dojo-vX.Y.Z.zip +``` + +Uncompress the archive + +``` +> unzip samourai-dojo-vX.Y.Z.zip -d . +``` + +Overwrite the dojo-app directory with the content of the archive + +``` +> cp -a samourai-dojo-vX.Y.Z/. dojo-app/ +``` + +#### 4.2.3/ Update Configuration (optional) + +Check the [release notes](https://code.samourai.io/dojo/samourai-dojo/-/blob/master/RELEASES.md) for a list of new features that may require to tune the value of new configuration options. + +If applicable, edit the templates files stored in `~/dojo-app/docker/my-dojo/conf/` and modify the values set for new configuration options. + + +#### 4.2.4/ Start Upgrade + +``` +> cd ~/dojo-app/docker/my-dojo +> ./dojo.sh upgrade +``` + +Confirm that you want to upgrade MyDojo with `Y` and `ENTER`. + +The shell script is going to rebuild the Docker containers. MyDojo will be automatically restarted after the containers have been rebuilt. + + +#### 4.2.5/ Cleanup + +``` +> cd ~ +> rm -rf samourai-dojo-vX.Y.Z +> rm samourai-dojo-vX.Y.Z.zip +``` From 5c5b95061aef93029085ea98fe5ef07ea7ec93fd Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 8 Jan 2021 14:11:25 +0100 Subject: [PATCH 28/54] code cleaning --- accounts/support-rest-api.js | 37 ------------------------------------ 1 file changed, 37 deletions(-) diff --git a/accounts/support-rest-api.js b/accounts/support-rest-api.js index 38e9df9..1e4010c 100644 --- a/accounts/support-rest-api.js +++ b/accounts/support-rest-api.js @@ -120,32 +120,9 @@ class SupportRestApi { */ _formatAddressInfoResult(info) { const res = info.toPojoExtended() - /*res._endpoints = [] - - if (info.tracked) { - res._endpoints.push({ - task: 'Rescan this address from remote sources', - url: `/${keys.prefixes.support}/address/${info.address}/rescan` - }) - } - - if (info.xpub != null) { - res._endpoints.push({ - task: 'Get information about the HD account that owns this address', - url: `/${keys.prefixes.support}/xpub/${info.xpub}/info` - }) - - res._endpoints.push({ - task: 'Rescan the whole HD account that owns this address', - url: `/${keys.prefixes.support}/xpub/${info.xpub}/rescan` - }) - }*/ - return JSON.stringify(res, null, 2) } - - /** * Rescan the blockchain for a given address * @param {object} req - http request object @@ -162,10 +139,6 @@ class SupportRestApi { const ret = { status: 'Rescan complete', - /*_endpoints: [{ - task: 'Get updated information about this address', - url: `/${keys.prefixes.support}/address/${address}/info` - }]*/ } await addrService.rescan(address) @@ -224,12 +197,6 @@ class SupportRestApi { */ _formatXpubInfoResult(info) { const res = info.toPojoExtended() - - /*res._endpoints = [{ - task: 'Rescan the whole HD account from remote sources', - url: `/${keys.prefixes.support}/xpub/${info.xpub}/rescan` - }]*/ - return JSON.stringify(res, null, 2) } @@ -249,10 +216,6 @@ class SupportRestApi { const ret = { status: 'Rescan complete', - /*_endpoints: [{ - task: 'Get updated information about this HD account', - url: `/${keys.prefixes.support}/xpub/${xpub}/info` - }]*/ } const gapLimit = req.query.gap != null ? parseInt(req.query.gap) : 0 From 3fa4f96240feb35a59686781dbd99dd5f6261863 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 8 Jan 2021 16:05:42 +0100 Subject: [PATCH 29/54] add new /support/xpub/delete api endpoint --- accounts/support-rest-api.js | 55 ++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/accounts/support-rest-api.js b/accounts/support-rest-api.js index 1e4010c..5bd293d 100644 --- a/accounts/support-rest-api.js +++ b/accounts/support-rest-api.js @@ -69,6 +69,14 @@ class SupportRestApi { HttpServer.sendAuthError ) + this.httpServer.app.get( + `/${keys.prefixes.support}/xpub/:xpub/delete`, + authMgr.checkHasAdminProfile.bind(authMgr), + this.validateArgsGetXpubDelete.bind(this), + this.getXpubDelete.bind(this), + HttpServer.sendAuthError + ) + this.httpServer.app.get( `/${keys.prefixes.support}/pairing/explorer`, authMgr.checkHasAdminProfile.bind(authMgr), @@ -246,6 +254,36 @@ class SupportRestApi { } } + /** + * Delete all data related to a hd account + * @param {object} req - http request object + * @param {object} res - http response object + */ + async getXpubDelete(req, res) { + try { + // Parse the entities passed as url params + const entities = apiHelper.parseEntities(req.params.xpub).xpubs + if (entities.length == 0) + return HttpServer.sendError(res, errors.xpub.INVALID) + + const xpub = entities[0] + + try { + await hdaService.deleteHdAccount(xpub) + HttpServer.sendOk(res) + } catch(e) { + HttpServer.sendError(res, e) + } + + } catch(e) { + HttpServer.sendError(res, errors.generic.GEN) + + } finally { + debugApi && Logger.info(`API : Completed GET /support/xpub/${req.params.xpub}/delete`) + } + } + + /** * Get pairing info */ @@ -329,6 +367,23 @@ class SupportRestApi { } } + /** + * Validate arguments related to GET xpub delete requests + * @param {object} req - http request object + * @param {object} res - http response object + * @param {function} next - next express middleware + */ + validateArgsGetXpubDelete(req, res, next) { + const isValidXpub = validator.isAlphanumeric(req.params.xpub) + + if (!isValidXpub) { + HttpServer.sendError(res, errors.body.INVDATA) + Logger.error(null, `API : SupportRestApi.validateArgsGetXpubDelete() : Invalid xpub ${req.params.xpub}`) + } else { + next() + } + } + /** * Validate arguments related to addresses requests * @param {object} req - http request object From f47017221699adf4a2bfce745ad068a572a947ed Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 8 Jan 2021 16:49:16 +0100 Subject: [PATCH 30/54] add support of xpub deletion from the dmt --- static/admin/css/style.css | 11 ++++++++ static/admin/dmt/xpubs-tools/xpubs-tools.html | 9 +++++++ static/admin/dmt/xpubs-tools/xpubs-tools.js | 27 +++++++++++++++++++ static/admin/lib/api-wrapper.js | 9 +++++++ 4 files changed, 56 insertions(+) diff --git a/static/admin/css/style.css b/static/admin/css/style.css index 7ffc507..a1da6e6 100644 --- a/static/admin/css/style.css +++ b/static/admin/css/style.css @@ -534,6 +534,17 @@ button { display: inline-block; } +#xpubs-deletion-actions span { + display: inline; +} + +#xpubs-deletion-actions input { + width: 50px; + margin-left: 5px; + margin-right: 5px; + display: inline-block; +} + #xpubs-tool-details #xpub-value { overflow: hidden; } diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.html b/static/admin/dmt/xpubs-tools/xpubs-tools.html index 2eeb6b9..fd7017e 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.html +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.html @@ -61,6 +61,7 @@
+
@@ -77,6 +78,14 @@ +
+
+ Do you want to delete this xpub? + + +
+
+
diff --git a/static/admin/dmt/xpubs-tools/xpubs-tools.js b/static/admin/dmt/xpubs-tools/xpubs-tools.js index 930663e..2ada7e5 100644 --- a/static/admin/dmt/xpubs-tools/xpubs-tools.js +++ b/static/admin/dmt/xpubs-tools/xpubs-tools.js @@ -11,8 +11,11 @@ const screenXpubsToolsScript = { $('#btn-xpub-search-go').click(() => {this.searchXpub()}) $('#btn-xpub-details-reset').click(() => {this.showSearchForm()}) $('#btn-xpub-details-rescan').click(() => {this.showRescanForm()}) + $('#btn-xpub-details-delete').click(() => {this.showDeletionForm()}) $('#btn-xpub-rescan-go').click(() => {this.rescanXpub()}) $('#btn-xpub-rescan-cancel').click(() => {this.hideRescanForm()}) + $('#btn-xpub-delete-go').click(() => {this.deleteXpub()}) + $('#btn-xpub-delete-cancel').click(() => {this.hideDeletionForm()}) $('#btn-xpub-import-go').click(() => {this.importXpub()}) $('#btn-xpub-details-retype').click(() => {this.showImportForm(true)}) $('#btn-xpub-import-cancel').click(() => {this.hideImportForm(this.isReimport)}) @@ -25,6 +28,7 @@ const screenXpubsToolsScript = { preparePage: function() { this.hideRescanForm() + this.hideDeletionForm() this.showSearchForm() $("#xpub").focus() }, @@ -128,6 +132,18 @@ const screenXpubsToolsScript = { } }, + deleteXpub: function() { + lib_msg.displayMessage('Deleting a xpub. Please wait...') + return lib_api.getXpubDelete(this.currentXpub) + .then(result => { + this.currentXpub = null + this.preparePage() + lib_msg.displayInfo('Xpub successfully deleted') + }).catch(e => { + lib_errors.processError(e) + }) + }, + checkRescanStatus: function(callback) { this.rescanStatusTimerId = setTimeout(() => { lib_api.getXpubRescanStatus(this.currentXpub) @@ -281,6 +297,17 @@ const screenXpubsToolsScript = { $('#xpubs-tool-actions').show() }, + showDeletionForm: function() { + $('#xpubs-tool-actions').hide() + $('#xpubs-deletion-actions').show() + lib_msg.cleanMessagesUi() + }, + + hideDeletionForm: function() { + $('#xpubs-deletion-actions').hide() + $('#xpubs-tool-actions').show() + }, + } screenScripts.set('#screen-xpubs-tools', screenXpubsToolsScript) diff --git a/static/admin/lib/api-wrapper.js b/static/admin/lib/api-wrapper.js index 47f492d..2fdae1d 100644 --- a/static/admin/lib/api-wrapper.js +++ b/static/admin/lib/api-wrapper.js @@ -110,6 +110,15 @@ const lib_api = { ) }, + /** + * Deletes a xpub + */ + getXpubDelete: function(xpub) { + let prefix = conf['prefixes']['support'] + let uri = this.baseUri + '/' + prefix + '/xpub/' + xpub + '/delete' + return this.sendGetUriEncoded(uri, {}) + }, + /** * Gets the status of a xpub rescan */ From a8adf26f626c338f22c22b3af7a2a353211c30ab Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 15 Jan 2021 14:57:51 +0100 Subject: [PATCH 31/54] upgrade bitcoind to bitcoin core 0.21.0 --- docker/my-dojo/bitcoin/Dockerfile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docker/my-dojo/bitcoin/Dockerfile b/docker/my-dojo/bitcoin/Dockerfile index 2be84c8..160ea50 100644 --- a/docker/my-dojo/bitcoin/Dockerfile +++ b/docker/my-dojo/bitcoin/Dockerfile @@ -5,10 +5,10 @@ FROM debian:buster # INSTALL BITCOIN ################################################################# ENV BITCOIN_HOME /home/bitcoin -ENV BITCOIN_VERSION 0.20.1 -ENV BITCOIN_URL https://bitcoincore.org/bin/bitcoin-core-0.20.1/bitcoin-0.20.1-x86_64-linux-gnu.tar.gz -ENV BITCOIN_SHA256 376194f06596ecfa40331167c39bc70c355f960280bd2a645fdbf18f66527397 -ENV BITCOIN_ASC_URL https://bitcoincore.org/bin/bitcoin-core-0.20.1/SHA256SUMS.asc +ENV BITCOIN_VERSION 0.21.0 +ENV BITCOIN_URL https://bitcoincore.org/bin/bitcoin-core-0.21.0/bitcoin-0.21.0-x86_64-linux-gnu.tar.gz +ENV BITCOIN_SHA256 da7766775e3f9c98d7a9145429f2be8297c2672fe5b118fd3dc2411fb48e0032 +ENV BITCOIN_ASC_URL https://bitcoincore.org/bin/bitcoin-core-0.21.0/SHA256SUMS.asc ENV BITCOIN_PGP_KS_URI hkp://keyserver.ubuntu.com:80 ENV BITCOIN_PGP_KEY 01EA5486DE18A882D4C2684590C8019E36C2E964 From 0d34fe36ddf0cdb1820ceaf13e0b5b83ad2c9159 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 15 Jan 2021 14:58:03 +0100 Subject: [PATCH 32/54] bump version of bitcoind container --- docker/my-dojo/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index 555cb45..9d9a43c 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -12,7 +12,7 @@ COMPOSE_CONVERT_WINDOWS_PATHS=1 DOJO_VERSION_TAG=1.9.0 DOJO_DB_VERSION_TAG=1.2.0 -DOJO_BITCOIND_VERSION_TAG=1.10.0 +DOJO_BITCOIND_VERSION_TAG=1.11.0 DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 DOJO_TOR_VERSION_TAG=1.6.0 From 05c1a1f27421aa3424890cdaf0309cb2826edb40 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 15 Jan 2021 15:11:09 +0100 Subject: [PATCH 33/54] replace hsv2bitcoind by hsv3bitcoind --- docker/my-dojo/bitcoin/restart.sh | 2 +- docker/my-dojo/dojo.sh | 6 +++--- docker/my-dojo/tor/restart.sh | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docker/my-dojo/bitcoin/restart.sh b/docker/my-dojo/bitcoin/restart.sh index f4efa2d..b89cdd8 100644 --- a/docker/my-dojo/bitcoin/restart.sh +++ b/docker/my-dojo/bitcoin/restart.sh @@ -32,7 +32,7 @@ bitcoind_options=( if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then bitcoind_options+=(-listen=1) bitcoind_options+=(-bind=172.28.1.5) - bitcoind_options+=(-externalip=$(cat /var/lib/tor/hsv2bitcoind/hostname)) + bitcoind_options+=(-externalip=$(cat /var/lib/tor/hsv3bitcoind/hostname)) fi if [ "$BITCOIND_RPC_EXTERNAL" == "on" ]; then diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index 96efbf7..4d71567 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -85,7 +85,7 @@ stop() { # Renewal of bitcoind onion address if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then if [ "$BITCOIND_EPHEMERAL_HS" = "on" ]; then - $( docker exec -it tor rm -rf /var/lib/tor/hsv2bitcoind ) &> /dev/null + $( docker exec -it tor rm -rf /var/lib/tor/hsv3bitcoind ) &> /dev/null fi fi # Stop the bitcoin daemon @@ -374,8 +374,8 @@ onion() { if [ "$BITCOIND_INSTALL" == "on" ]; then if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then - V2_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv2bitcoind/hostname ) - echo "Your local bitcoind (do not share) = $V2_ADDR_BTCD" + V3_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv3bitcoind/hostname ) + echo "Your local bitcoind (do not share) = $V3_ADDR_BTCD" echo " " fi fi diff --git a/docker/my-dojo/tor/restart.sh b/docker/my-dojo/tor/restart.sh index e44a98b..18f72a2 100644 --- a/docker/my-dojo/tor/restart.sh +++ b/docker/my-dojo/tor/restart.sh @@ -23,8 +23,8 @@ tor_options=( if [ "$BITCOIND_INSTALL" == "on" ]; then if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then - tor_options+=(--HiddenServiceDir /var/lib/tor/hsv2bitcoind) - tor_options+=(--HiddenServiceVersion 2) + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv3bitcoind) + tor_options+=(--HiddenServiceVersion 3) tor_options+=(--HiddenServicePort "8333 172.28.1.5:8333") tor_options+=(--HiddenServiceDirGroupReadable 1) fi From 38e45dd6422185cf11b4e90944dc78ab549bfad9 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 15 Jan 2021 15:11:31 +0100 Subject: [PATCH 34/54] delete hsv2bitcoind --- docker/my-dojo/install/upgrade-scripts.sh | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/docker/my-dojo/install/upgrade-scripts.sh b/docker/my-dojo/install/upgrade-scripts.sh index c342ac1..ff810df 100755 --- a/docker/my-dojo/install/upgrade-scripts.sh +++ b/docker/my-dojo/install/upgrade-scripts.sh @@ -69,7 +69,7 @@ update_config_files() { update_config_file ./conf/docker-whirlpool.conf ./conf/docker-whirlpool.conf.tpl echo "Initialized docker-whirlpool.conf" - # Initialize config files for nginx and the maintenance tool + # Initialize config files for nginx and the maintenance tool if [ "$EXPLORER_INSTALL" == "on" ]; then cp ./nginx/explorer.conf ./nginx/dojo-explorer.conf else @@ -106,7 +106,7 @@ update_config_file() { cp -p $1 "$1.save" cp -p $2 $1 - while IFS='=' read -r key val ; do + while IFS='=' read -r key val ; do if [[ $OSTYPE == darwin* ]]; then sed -i "" "s~$key=.*~$key=$val~g" "$1" else @@ -179,11 +179,18 @@ cleanup() { if [ -f ./bitcoin/bitcoin.conf ]; then rm ./bitcoin/bitcoin.conf fi - + } # Post start clean-up post_start_cleanup() { + ################# + # Clean-up v1.9.0 + ################# + + # Remove /var/lib/tor/hsv2bitcoind from tor volume + docker exec -it tor rm -rf /var/lib/tor/hsv2bitcoind + ################# # Clean-up v1.6.0 ################# From 217b4cc20830dceb0487f1c5f90038d41b91f469 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 15 Jan 2021 16:52:10 +0100 Subject: [PATCH 35/54] adapt value of rpcallowip --- docker/my-dojo/bitcoin/restart.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/bitcoin/restart.sh b/docker/my-dojo/bitcoin/restart.sh index b89cdd8..b04501d 100644 --- a/docker/my-dojo/bitcoin/restart.sh +++ b/docker/my-dojo/bitcoin/restart.sh @@ -16,7 +16,7 @@ bitcoind_options=( -minrelaytxfee=$BITCOIND_MIN_RELAY_TX_FEE -port=8333 -proxy=172.28.1.4:9050 - -rpcallowip=::/0 + -rpcallowip=0.0.0.0/0 -rpcbind=172.28.1.5 -rpcpassword=$BITCOIND_RPC_PASSWORD -rpcport=28256 From 5920e75a78ba574e6b79d0ad59a5f9afd271ab58 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 15 Jan 2021 17:52:22 +0100 Subject: [PATCH 36/54] upgrade explorer to btc-rpc-explorer 2.1.0 --- docker/my-dojo/explorer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/explorer/Dockerfile b/docker/my-dojo/explorer/Dockerfile index 23ac535..c41c689 100644 --- a/docker/my-dojo/explorer/Dockerfile +++ b/docker/my-dojo/explorer/Dockerfile @@ -3,7 +3,7 @@ FROM node:12-buster ENV APP_DIR /home/node/app ENV EXPLORER_URL https://github.com/janoside/btc-rpc-explorer/archive -ENV EXPLORER_VERSION 2.0.2 +ENV EXPLORER_VERSION 2.1.0 # Install netcat RUN set -ex && \ From 994679dc43e32783a666902208bfacf332523d38 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 21 Jan 2021 18:19:39 +0100 Subject: [PATCH 37/54] upgrad tor to v0.4.4.6 --- docker/my-dojo/tor/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/my-dojo/tor/Dockerfile b/docker/my-dojo/tor/Dockerfile index eefa0d8..4f64d1b 100644 --- a/docker/my-dojo/tor/Dockerfile +++ b/docker/my-dojo/tor/Dockerfile @@ -3,7 +3,7 @@ FROM debian:buster ENV TOR_HOME /var/lib/tor ENV TOR_URL https://archive.torproject.org/tor-package-archive ENV TOR_MIRROR_URL https://tor.eff.org/dist -ENV TOR_VERSION 0.4.2.7 +ENV TOR_VERSION 0.4.4.6 ENV TOR_GPG_KS_URI hkp://keyserver.ubuntu.com:80 ENV TOR_GPG_KEY1 0xEB5A896A28988BF5 ENV TOR_GPG_KEY2 0xC218525819F78451 @@ -94,7 +94,7 @@ RUN chown tor:tor /wait-for-it.sh && \ chmod u+x /wait-for-it.sh && \ chmod g+x /wait-for-it.sh -# Expose socks port +# Expose socks port EXPOSE 9050 # Switch to user tor From 466dbc52834052eec6d1b269f5b38baf8518dc62 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 21 Jan 2021 18:19:51 +0100 Subject: [PATCH 38/54] bump version of tor container --- docker/my-dojo/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/.env b/docker/my-dojo/.env index 9d9a43c..cd27eb6 100644 --- a/docker/my-dojo/.env +++ b/docker/my-dojo/.env @@ -15,7 +15,7 @@ DOJO_DB_VERSION_TAG=1.2.0 DOJO_BITCOIND_VERSION_TAG=1.11.0 DOJO_NODEJS_VERSION_TAG=1.9.0 DOJO_NGINX_VERSION_TAG=1.5.0 -DOJO_TOR_VERSION_TAG=1.6.0 +DOJO_TOR_VERSION_TAG=1.7.0 DOJO_EXPLORER_VERSION_TAG=1.4.0 DOJO_INDEXER_VERSION_TAG=1.2.0 DOJO_WHIRLPOOL_VERSION_TAG=1.3.0 From 9dde28de615a0e3c6f2885a93e4ad5a734ac333a Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 28 Jan 2021 10:51:55 +0100 Subject: [PATCH 39/54] build addrindexrs with --locked argument --- docker/my-dojo/indexer/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/indexer/Dockerfile b/docker/my-dojo/indexer/Dockerfile index f267c72..ea3b26a 100644 --- a/docker/my-dojo/indexer/Dockerfile +++ b/docker/my-dojo/indexer/Dockerfile @@ -36,7 +36,7 @@ RUN cd "$INDEXER_HOME" && \ git checkout "tags/v$INDEXER_VERSION" RUN cd "$INDEXER_HOME/addrindexrs" && \ - cargo install --path . + cargo install --locked --path . EXPOSE 50001 From 8dd3206fc72e7fdc758e0fb0279b5c6fd77345aa Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Fri, 29 Jan 2021 09:31:35 +0100 Subject: [PATCH 40/54] upgrade tor to v0.4.4.6 --- docker/my-dojo/whirlpool/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/whirlpool/Dockerfile b/docker/my-dojo/whirlpool/Dockerfile index 396c2c9..25ae23f 100644 --- a/docker/my-dojo/whirlpool/Dockerfile +++ b/docker/my-dojo/whirlpool/Dockerfile @@ -21,7 +21,7 @@ RUN set -ex && \ # Install Tor ENV WHIRLPOOL_TOR_URL https://archive.torproject.org/tor-package-archive ENV WHIRLPOOL_TOR_MIRROR_URL https://tor.eff.org/dist -ENV WHIRLPOOL_TOR_VERSION 0.4.2.7 +ENV WHIRLPOOL_TOR_VERSION 0.4.4.6 ENV WHIRLPOOL_TOR_GPG_KS_URI hkp://keyserver.ubuntu.com:80 ENV WHIRLPOOL_TOR_GPG_KEY1 0xEB5A896A28988BF5 ENV WHIRLPOOL_TOR_GPG_KEY2 0xC218525819F78451 From a7f26de9651c15057353ddf5e91136b0df0d03ba Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Mon, 1 Feb 2021 16:33:20 +0100 Subject: [PATCH 41/54] return exit code 2 if install or upgrade is cancelled --- docker/my-dojo/dojo.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index 4d71567..10a6d3c 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -211,6 +211,8 @@ install() { echo -e "\nInstallation of Dojo failed. See the above error message." exit $buildResult fi + else + exit 2 fi } @@ -345,6 +347,8 @@ upgrade() { echo -e "\nUpgrade of Dojo failed. See the above error message." exit $buildResult fi + else + exit 2 fi } From 33592eefeecd44465bb05c69b630dd3719a9a9a8 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Tue, 2 Feb 2021 12:44:27 +0100 Subject: [PATCH 42/54] return a 0 feerate if bitcoind doesn't return an estimate --- lib/bitcoind-rpc/fees.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/bitcoind-rpc/fees.js b/lib/bitcoind-rpc/fees.js index 386ecd2..fb4174d 100644 --- a/lib/bitcoind-rpc/fees.js +++ b/lib/bitcoind-rpc/fees.js @@ -14,7 +14,7 @@ const latestBlock = require('./latest-block') /** - * A singleton providing information about network fees + * A singleton providing information about network fees */ class Fees { @@ -56,10 +56,10 @@ class Fees { await util.seriesCall(this.targets, async tgt => { try { const level = await this.rpcClient.cmd('estimatesmartfee', tgt, this.feeType) - this.fees[tgt] = Math.round(level.feerate * 1e5) + this.fees[tgt] = (level.errors && level.errors.length > 0) ? 0 : Math.round(level.feerate * 1e5) } catch(e) { Logger.error(e, 'Bitcoind RPC : Fees.refresh()') - delete this.fees[tgt] + this.fees[tgt] = 0 } }) From d4e29135a7a19736e1943b88ae6868944c88be5e Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Wed, 3 Feb 2021 14:56:26 +0100 Subject: [PATCH 43/54] add new WHIRLPOOL_COORDINATOR_ONION config option --- docker/my-dojo/conf/docker-whirlpool.conf.tpl | 4 ++++ docker/my-dojo/whirlpool/restart.sh | 7 ++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docker/my-dojo/conf/docker-whirlpool.conf.tpl b/docker/my-dojo/conf/docker-whirlpool.conf.tpl index 762681f..dbf5e9b 100644 --- a/docker/my-dojo/conf/docker-whirlpool.conf.tpl +++ b/docker/my-dojo/conf/docker-whirlpool.conf.tpl @@ -10,6 +10,10 @@ WHIRLPOOL_INSTALL=off # Value: on | off WHIRLPOOL_RESYNC=off +# Contact coordinator through its onion address or clearnet address +# Set to on for onion address, set to off for clearnet address +# Value: on | off +WHIRLPOOL_COORDINATOR_ONION=on # # EXPERT SETTINGS diff --git a/docker/my-dojo/whirlpool/restart.sh b/docker/my-dojo/whirlpool/restart.sh index 6c69eea..406ba3e 100644 --- a/docker/my-dojo/whirlpool/restart.sh +++ b/docker/my-dojo/whirlpool/restart.sh @@ -9,7 +9,6 @@ whirlpool_options=( --cli.tor=true --cli.torConfig.executable=/usr/local/bin/tor --cli.torConfig.coordinator.enabled=true - --cli.torConfig.coordinator.onion=true --cli.torConfig.backend.enabled=false --cli.torConfig.backend.onion=false --cli.mix.liquidityClient=false @@ -23,6 +22,12 @@ else whirlpool_options+=(--cli.dojo.url="http://172.30.1.3:80/v2/") fi +if [ "$WHIRLPOOL_COORDINATOR_ONION" == "on" ]; then + whirlpool_options+=(--cli.torConfig.coordinator.onion=true) +else + whirlpool_options+=(--cli.torConfig.coordinator.onion=false) +fi + if [ "$WHIRLPOOL_RESYNC" == "on" ]; then whirlpool_options+=(--resync) fi From 36e4db5887de6f0cc254119e9742b97fe1fee8f5 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Wed, 3 Feb 2021 16:10:27 +0100 Subject: [PATCH 44/54] add v2 onion addresses for explorer and whirlpool --- docker/my-dojo/dojo.sh | 77 ++++++++++++++++++++++++++--------- docker/my-dojo/tor/restart.sh | 10 +++++ 2 files changed, 68 insertions(+), 19 deletions(-) diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index 10a6d3c..b3cddad 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -352,34 +352,69 @@ upgrade() { fi } -# Display the onion address +# Display the onion addresses onion() { + version=3 + + # Extract version arguments + if [ $# -gt 0 ]; then + for option in $@ + do + case "$option" in + v2 ) version=2 ;; + v3 ) version=3 ;; + * ) break ;; + esac + done + fi + echo " " echo "WARNING: Do not share these onion addresses with anyone!" echo " To allow another person to use this Dojo with their Samourai Wallet," echo " you should share the QRCodes provided by the Maintenance Tool." echo " " - V3_ADDR=$( docker exec -it tor cat /var/lib/tor/hsv3dojo/hostname ) - echo "Dojo API and Maintenance Tool = $V3_ADDR" - echo " " - - if [ "$EXPLORER_INSTALL" == "on" ]; then - V3_ADDR_EXPLORER=$( docker exec -it tor cat /var/lib/tor/hsv3explorer/hostname ) - echo "Block Explorer = $V3_ADDR_EXPLORER" + if [ $version -eq 3 ]; then + # V3 onion addresses + V3_ADDR=$( docker exec -it tor cat /var/lib/tor/hsv3dojo/hostname ) + echo "Dojo API and Maintenance Tool = $V3_ADDR" echo " " - fi - if [ "$WHIRLPOOL_INSTALL" == "on" ]; then - V3_ADDR_WHIRLPOOL=$( docker exec -it tor cat /var/lib/tor/hsv3whirlpool/hostname ) - echo "Your private Whirlpool client (do not share) = $V3_ADDR_WHIRLPOOL" + if [ "$EXPLORER_INSTALL" == "on" ]; then + V3_ADDR_EXPLORER=$( docker exec -it tor cat /var/lib/tor/hsv3explorer/hostname ) + echo "Block Explorer = $V3_ADDR_EXPLORER" + echo " " + fi + + if [ "$WHIRLPOOL_INSTALL" == "on" ]; then + V3_ADDR_WHIRLPOOL=$( docker exec -it tor cat /var/lib/tor/hsv3whirlpool/hostname ) + echo "Your private Whirlpool client (do not share) = $V3_ADDR_WHIRLPOOL" + echo " " + fi + + if [ "$BITCOIND_INSTALL" == "on" ]; then + if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + V3_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv3bitcoind/hostname ) + echo "Your local bitcoind (do not share) = $V3_ADDR_BTCD" + echo " " + fi + fi + + else + # v2 onion addresses + V2_ADDR=$( docker exec -it tor cat /var/lib/tor/hsv2dojo/hostname ) + echo "Dojo API and Maintenance Tool = $V2_ADDR" echo " " - fi - if [ "$BITCOIND_INSTALL" == "on" ]; then - if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then - V3_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv3bitcoind/hostname ) - echo "Your local bitcoind (do not share) = $V3_ADDR_BTCD" + if [ "$EXPLORER_INSTALL" == "on" ]; then + V2_ADDR_EXPLORER=$( docker exec -it tor cat /var/lib/tor/hsv2explorer/hostname ) + echo "Block Explorer = $V2_ADDR_EXPLORER" + echo " " + fi + + if [ "$WHIRLPOOL_INSTALL" == "on" ]; then + V2_ADDR_WHIRLPOOL=$( docker exec -it tor cat /var/lib/tor/hsv2whirlpool/hostname ) + echo "Your private Whirlpool client (do not share) = $V2_ADDR_WHIRLPOOL" echo " " fi fi @@ -517,7 +552,11 @@ help() { echo " Available options:" echo " -n [VALUE] : display the last VALUE lines" echo " " - echo " onion Display the Tor onion address allowing your wallet to access your dojo." + echo " onion [version] Display the Tor onion addresses allowing your wallet to access your dojo." + echo " " + echo " Available versions:" + echo " v2: display Tor v2 onion addresses" + echo " v3 (default): display Tor v3 onion addresses" echo " " echo " restart Restart your dojo." echo " " @@ -611,7 +650,7 @@ case "$subcommand" in logs "$module" $numlines ;; onion ) - onion + onion "$@" ;; restart ) restart diff --git a/docker/my-dojo/tor/restart.sh b/docker/my-dojo/tor/restart.sh index 18f72a2..26e66ef 100644 --- a/docker/my-dojo/tor/restart.sh +++ b/docker/my-dojo/tor/restart.sh @@ -31,6 +31,11 @@ if [ "$BITCOIND_INSTALL" == "on" ]; then fi if [ "$EXPLORER_INSTALL" == "on" ]; then + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv2explorer) + tor_options+=(--HiddenServiceVersion 2) + tor_options+=(--HiddenServicePort "80 172.29.1.3:9080") + tor_options+=(--HiddenServiceDirGroupReadable 1) + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv3explorer) tor_options+=(--HiddenServiceVersion 3) tor_options+=(--HiddenServicePort "80 172.29.1.3:9080") @@ -38,6 +43,11 @@ if [ "$EXPLORER_INSTALL" == "on" ]; then fi if [ "$WHIRLPOOL_INSTALL" == "on" ]; then + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv2whirlpool) + tor_options+=(--HiddenServiceVersion 2) + tor_options+=(--HiddenServicePort "80 172.29.1.3:8898") + tor_options+=(--HiddenServiceDirGroupReadable 1) + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv3whirlpool) tor_options+=(--HiddenServiceVersion 3) tor_options+=(--HiddenServicePort "80 172.29.1.3:8898") From d5ea5533d605b56b91ed67fb74b561158e91f07e Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 11:27:31 +0100 Subject: [PATCH 45/54] return exit code 1 instead of 2 for aborted install & upgrade --- docker/my-dojo/dojo.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index b3cddad..1d39486 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -212,7 +212,7 @@ install() { exit $buildResult fi else - exit 2 + exit 1 fi } @@ -348,7 +348,7 @@ upgrade() { exit $buildResult fi else - exit 2 + exit 1 fi } From ba6c976cac1d9c808b89997c872a025f43a7c5e0 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 14:39:35 +0100 Subject: [PATCH 46/54] reactivate tor v2 hidden service for bitcoind --- docker/my-dojo/bitcoin/restart.sh | 1 + docker/my-dojo/dojo.sh | 9 +++++++++ docker/my-dojo/install/upgrade-scripts.sh | 7 ------- docker/my-dojo/tor/restart.sh | 5 +++++ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/docker/my-dojo/bitcoin/restart.sh b/docker/my-dojo/bitcoin/restart.sh index b04501d..db14dd3 100644 --- a/docker/my-dojo/bitcoin/restart.sh +++ b/docker/my-dojo/bitcoin/restart.sh @@ -32,6 +32,7 @@ bitcoind_options=( if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then bitcoind_options+=(-listen=1) bitcoind_options+=(-bind=172.28.1.5) + bitcoind_options+=(-externalip=$(cat /var/lib/tor/hsv2bitcoind/hostname)) bitcoind_options+=(-externalip=$(cat /var/lib/tor/hsv3bitcoind/hostname)) fi diff --git a/docker/my-dojo/dojo.sh b/docker/my-dojo/dojo.sh index 1d39486..3798f38 100755 --- a/docker/my-dojo/dojo.sh +++ b/docker/my-dojo/dojo.sh @@ -85,6 +85,7 @@ stop() { # Renewal of bitcoind onion address if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then if [ "$BITCOIND_EPHEMERAL_HS" = "on" ]; then + $( docker exec -it tor rm -rf /var/lib/tor/hsv2bitcoind ) &> /dev/null $( docker exec -it tor rm -rf /var/lib/tor/hsv3bitcoind ) &> /dev/null fi fi @@ -417,6 +418,14 @@ onion() { echo "Your private Whirlpool client (do not share) = $V2_ADDR_WHIRLPOOL" echo " " fi + + if [ "$BITCOIND_INSTALL" == "on" ]; then + if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + V2_ADDR_BTCD=$( docker exec -it tor cat /var/lib/tor/hsv2bitcoind/hostname ) + echo "Your local bitcoind (do not share) = $V2_ADDR_BTCD" + echo " " + fi + fi fi } diff --git a/docker/my-dojo/install/upgrade-scripts.sh b/docker/my-dojo/install/upgrade-scripts.sh index ff810df..9669cd1 100755 --- a/docker/my-dojo/install/upgrade-scripts.sh +++ b/docker/my-dojo/install/upgrade-scripts.sh @@ -184,13 +184,6 @@ cleanup() { # Post start clean-up post_start_cleanup() { - ################# - # Clean-up v1.9.0 - ################# - - # Remove /var/lib/tor/hsv2bitcoind from tor volume - docker exec -it tor rm -rf /var/lib/tor/hsv2bitcoind - ################# # Clean-up v1.6.0 ################# diff --git a/docker/my-dojo/tor/restart.sh b/docker/my-dojo/tor/restart.sh index 26e66ef..ef1392b 100644 --- a/docker/my-dojo/tor/restart.sh +++ b/docker/my-dojo/tor/restart.sh @@ -23,6 +23,11 @@ tor_options=( if [ "$BITCOIND_INSTALL" == "on" ]; then if [ "$BITCOIND_LISTEN_MODE" == "on" ]; then + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv2bitcoind) + tor_options+=(--HiddenServiceVersion 2) + tor_options+=(--HiddenServicePort "8333 172.28.1.5:8333") + tor_options+=(--HiddenServiceDirGroupReadable 1) + tor_options+=(--HiddenServiceDir /var/lib/tor/hsv3bitcoind) tor_options+=(--HiddenServiceVersion 3) tor_options+=(--HiddenServicePort "8333 172.28.1.5:8333") From 9fe22a356625e0c1aeb18691d617af9118990c84 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 16:22:28 +0100 Subject: [PATCH 47/54] update .gitignore --- .gitignore | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 28e5ac1..eaac5fa 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,13 @@ db-scripts/updates/ db-scripts/1_db.sql db-scripts/2_update.sql -docker/my-dojo/conf/docker-bitcoind.conf -docker/my-dojo/conf/docker-mysql.conf -docker/my-dojo/conf/docker-node.conf +docker/my-dojo/conf/*.conf +docker/my-dojo/conf/*.conf.save keys/index.js keys/sslcert/ node_modules/ private-tests/ static/admin/conf/index.js +static/admin-legacy static/admin-legacy/ *.log -static/admin-legacy From f3dad633ccfe5b07bacae96caaa7b7720525f6dd Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 16:40:10 +0100 Subject: [PATCH 48/54] switch to dist.torproject.org --- docker/my-dojo/tor/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/tor/Dockerfile b/docker/my-dojo/tor/Dockerfile index 4f64d1b..a84c59e 100644 --- a/docker/my-dojo/tor/Dockerfile +++ b/docker/my-dojo/tor/Dockerfile @@ -1,7 +1,7 @@ FROM debian:buster ENV TOR_HOME /var/lib/tor -ENV TOR_URL https://archive.torproject.org/tor-package-archive +ENV TOR_URL https://dist.torproject.org ENV TOR_MIRROR_URL https://tor.eff.org/dist ENV TOR_VERSION 0.4.4.6 ENV TOR_GPG_KS_URI hkp://keyserver.ubuntu.com:80 From d056c0e9d6307e2554118b988896201c27038b5a Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 16:41:39 +0100 Subject: [PATCH 49/54] upgrade tor to v0.4.4.7 --- docker/my-dojo/tor/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/tor/Dockerfile b/docker/my-dojo/tor/Dockerfile index a84c59e..f21ce2c 100644 --- a/docker/my-dojo/tor/Dockerfile +++ b/docker/my-dojo/tor/Dockerfile @@ -3,7 +3,7 @@ FROM debian:buster ENV TOR_HOME /var/lib/tor ENV TOR_URL https://dist.torproject.org ENV TOR_MIRROR_URL https://tor.eff.org/dist -ENV TOR_VERSION 0.4.4.6 +ENV TOR_VERSION 0.4.4.7 ENV TOR_GPG_KS_URI hkp://keyserver.ubuntu.com:80 ENV TOR_GPG_KEY1 0xEB5A896A28988BF5 ENV TOR_GPG_KEY2 0xC218525819F78451 From 280ca3a157e78a22a191e8da2dd3927326850d7f Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 16:42:27 +0100 Subject: [PATCH 50/54] switch to dist.torproject.org --- docker/my-dojo/whirlpool/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/whirlpool/Dockerfile b/docker/my-dojo/whirlpool/Dockerfile index 25ae23f..dd72cf3 100644 --- a/docker/my-dojo/whirlpool/Dockerfile +++ b/docker/my-dojo/whirlpool/Dockerfile @@ -19,7 +19,7 @@ RUN set -ex && \ mkdir -p "$WHIRLPOOL_DIR" # Install Tor -ENV WHIRLPOOL_TOR_URL https://archive.torproject.org/tor-package-archive +ENV WHIRLPOOL_TOR_URL https://dist.torproject.org ENV WHIRLPOOL_TOR_MIRROR_URL https://tor.eff.org/dist ENV WHIRLPOOL_TOR_VERSION 0.4.4.6 ENV WHIRLPOOL_TOR_GPG_KS_URI hkp://keyserver.ubuntu.com:80 From 44da7321530f069b5783591b85a2e441fafb7f6d Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 16:43:33 +0100 Subject: [PATCH 51/54] upgrade whirlpool container to tor 0.4.4.7 --- docker/my-dojo/whirlpool/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/my-dojo/whirlpool/Dockerfile b/docker/my-dojo/whirlpool/Dockerfile index dd72cf3..12e6c90 100644 --- a/docker/my-dojo/whirlpool/Dockerfile +++ b/docker/my-dojo/whirlpool/Dockerfile @@ -21,7 +21,7 @@ RUN set -ex && \ # Install Tor ENV WHIRLPOOL_TOR_URL https://dist.torproject.org ENV WHIRLPOOL_TOR_MIRROR_URL https://tor.eff.org/dist -ENV WHIRLPOOL_TOR_VERSION 0.4.4.6 +ENV WHIRLPOOL_TOR_VERSION 0.4.4.7 ENV WHIRLPOOL_TOR_GPG_KS_URI hkp://keyserver.ubuntu.com:80 ENV WHIRLPOOL_TOR_GPG_KEY1 0xEB5A896A28988BF5 ENV WHIRLPOOL_TOR_GPG_KEY2 0xC218525819F78451 From 2d80aa9ed0dee9208cfdbb8956838094c64c2141 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Thu, 4 Feb 2021 18:56:29 +0100 Subject: [PATCH 52/54] update release notes --- RELEASES.md | 163 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 163 insertions(+) diff --git a/RELEASES.md b/RELEASES.md index 7c697d1..f41079b 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -3,6 +3,8 @@ ## Releases ## +- [v1.9.0](#1_9_0) +- [v1.8.1](#1_8_1) - [v1.8.0](#1_8_0) - [v1.7.0](#1_7_0) - [v1.6.0](#1_6_0) @@ -14,6 +16,167 @@ - [v1.1.0](#1_1_0) + + +## Samourai Dojo v1.9.0 ## + + +### Notable changes ### + + +#### Maintenance Tool: multiple UX improvements #### + +*Status screen* + +The status screen now displays information related to the Dojo database and to the data source used by Dojo for its imports and rescans. This screen provides a high level view of the state of the Dojo instance, that can be shared for support. + +*XPUB Tool* + +- Progress made is now displayed during an import or a rescan. +- New feature allowing to delete a XPUB tracked by Dojo. +- Improved management of timeouts by the authentication system. + + +#### Dojo Shell Script: improvements #### + +- Script automatically stops if build fails during install/upgrade operation. +- Script returns a not null exit code if build fails or if install/upgrade operation is cancelled. +- Dojo is automatically stopped if an upgrade operation is launched with Dojo up and running. +- A cleanup of old Dojo versions is automatically processed at the end of successful upgrade operations. + + +#### New configuration options #### + +Addition of two new configuration options: + +- BITCOIND_LISTEN_MODE (in docker-bitcoind.conf): When set to `off`, the fullnode will refuse incoming connections. Default = `on`. +- WHIRLPOOL_COORDINATOR_ONION (in docker-whirlpool.conf): When set to `on`, whirlpool-cli will contact the coordinator through its onion address. When set to `off`, clearnet address will be used (through Tor). Default = `on`. + + +#### Extended support Tor hidden services #### + +Dojo now provides a v2 and v3 hidden service for: + +- Dojo Maintenance Tool and API +- Whirlpool CLI +- Bitcoind +- Explorer + +Tor v3 onion addresses are recommended but v2 addresses can be used in the case of new attacks disrupting v3 hidden services. + +These onion addresses can be retrieved thanks to the `onion` command of the Dojo Shell Script + +''' +# Display Tor v3 onion addresses (default) +> ./dojo.h onion + +# Display Tor v3 onion addresses +> ./dojo.h onion v3 + +# Display Tor v2 onion addresses +> ./dojo.h onion v2 +''' + + +#### Upgrade of bitcoind to v0.21.0 #### + +Upgrade to Bitcoin Core v0.21.0 + + +#### Upgrade of whirlpool to v0.10.9 #### + +Upgrade to whirlpool-cli 0.10.9 + + +#### Upgrade of explorer to v2.1.0 #### + +Upgrade to btc-rpc-explorer 2.1.0 + + +#### Upgrade of tor to v0.4.4.7 #### + +Tor 0.4.4.7 fixes and mitigates multiple issues, including one that made v3 onion services more susceptible to denial-of-service attacks. + + +#### Upgrade of indexer to v0.4.0 #### + +Upgrade to addrindexrs v0.4.0 + + +### Change log ### + + +#### MyDojo #### + +- [#mr165](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/165) improve dmt ux +- [#mr166](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/166) add new configuration property BITCOIND_LISTEN_MODE +- [#mr167](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/167) upgrade explorer to btc-rpc-explorer 2.0.2 +- [#mr168](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/168) add new getChaintipHeight() method to remote importer and data sources +- [#mr170](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/170) add indexer info to /status endpoint +- [#mr171](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/171) add db and indexer blocks to status screen of dmt +- [#mr172](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/172) upgrade indexer to addrindexrs 0.4.0 +- [#mr174](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/174) upgrade whirlpool to whirlpool-cli 0.10.9 +- [#mr175](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/175) track and display progress of import/rescan +- [#mr178](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/178) improve dojo shell script +- [#mr179](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/179) update samourai logo +- [#mr181](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/181) add support of xpub deletion from the dmt +- [#mr182](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/182) upgrade bitcoin container with bitcoin core 0.21.0 +- [#mr183](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/183) upgrade explorer to btc-rpc-explorer 2.1.0 +- [#mr184](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/184) upgrade tor to v0.4.4.6 +- [#mr186](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/186) upgrade tor to v0.4.4.6 +- [#mr188](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/188) return exit code 2 if install or upgrade is cancelled +- [#mr190](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/190) add new WHIRLPOOL_COORDINATOR_ONION config option +- [#mr191](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/191) add v2 onion addresses for explorer and whirlpool +- [#mr192](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/192) return exit code 1 instead of 2 for aborted install & upgrade +- [#mr193](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/193) reactivate tor v2 hidden service for bitcoind +- [#mr194](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/194) upgrade tor to v0.4.4.7 +- [9fe22a35](https://code.samourai.io/dojo/samourai-dojo/-/commit/9fe22a356625e0c1aeb18691d617af9118990c84) update .gitignore + + +#### Bug fixes #### + +- [#mr176](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/176) check that jqxhr['responseJSON']['error'] is a string +- [#mr177](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/177) prevent restart of bitcoin container if bitcoind fails +- [#mr185](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/185) build addrindexrs with --locked argument +- [#mr189](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/189) return a 0 feerate if bitcoind doesn't return an estimate + + +#### Security #### + +- [#mr173](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/173) replace request-promise-native by axios + + +#### Documentation #### + +- [#mr180](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/180) add a detailed installation and upgrade guide for ubuntu + + +#### Credits ### + +- BTCxZelko +- flatcloud0b3 +- kenshin-samourai +- LaurentMT +- likewhoa + + + + +## Samourai Dojo v1.8.1 ## + +### Notable changes ### + +#### Upgrade of tor to v0.4.5.4-rc #### + +Upgrade to Tor v0.4.5.4-rc for a fix mitigating attacks on Tor v3 hidden services + +### Change log ### + +#### Security #### + +- [320f8cbf](https://code.samourai.io/dojo/samourai-dojo/-/commit/320f8cbfbe5b6a1e59f5154110216758ed08b9dc) upgrade tor to v0.4.5.4 + + ## Samourai Dojo v1.8.0 ## From 247ca59402a29bde72e12c637e66c55a671f5515 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Mon, 8 Feb 2021 14:20:48 +0100 Subject: [PATCH 53/54] bump block height defining ibd mode --- tracker/blockchain-processor.js | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/tracker/blockchain-processor.js b/tracker/blockchain-processor.js index b5f7069..f0ee571 100644 --- a/tracker/blockchain-processor.js +++ b/tracker/blockchain-processor.js @@ -60,7 +60,7 @@ class BlockchainProcessor extends AbstractProcessor { const daemonNbHeaders = info.headers // Consider that we are in IBD mode if Dojo is far in the past (> 13,000 blocks) - this.isIBD = (highest.blockHeight < 612000) || (highest.blockHeight < daemonNbHeaders - 13000) + this.isIBD = (highest.blockHeight < 655000) || (highest.blockHeight < daemonNbHeaders - 13000) if (this.isIBD) return this.catchupIBDMode() @@ -169,7 +169,7 @@ class BlockchainProcessor extends AbstractProcessor { try { const hash = await this.client.getblockhash(height) const header = await this.client.getblockheader(hash) - return this.processBlock(header) + return this.processBlock(header) } catch(e) { Logger.error(e, 'Tracker : BlockchainProcessor.catchupNormalMode()') process.exit() @@ -206,25 +206,25 @@ class BlockchainProcessor extends AbstractProcessor { /** * Upon receipt of a new block hash, retrieve the block header from bitcoind via * RPC. Continue pulling block headers back through the chain until the database - * contains header.previousblockhash, adding the headers to a stack. If the - * previousblockhash is not found on the first call, this is either a chain + * contains header.previousblockhash, adding the headers to a stack. If the + * previousblockhash is not found on the first call, this is either a chain * re-org or the tracker missed blocks during a shutdown. * * Once the chain has bottomed out with a known block in the database, delete * all known database transactions confirmed in blocks at heights greater than - * the last known block height. These transactions are orphaned but may reappear - * in the new chain. Notify relevant accounts of balance updates / + * the last known block height. These transactions are orphaned but may reappear + * in the new chain. Notify relevant accounts of balance updates / * transaction confirmation counts. * * Delete block entries not on the main chain. * - * Forward-scan through the block headers, pulling the full raw block hex via + * Forward-scan through the block headers, pulling the full raw block hex via * RPC. The raw block contains all transactions and is parsed by bitcoinjs-lib. - * Add the block to the database. Run checkTransaction for each transaction in + * Add the block to the database. Run checkTransaction for each transaction in * the block that is not in the database. Confirm all transactions in the block. * * After each block, query bitcoin against all database unconfirmed outputs - * to see if they remain in the mempool or have been confirmed in blocks. + * to see if they remain in the mempool or have been confirmed in blocks. * Malleated transactions entering the wallet will disappear from the mempool on * block confirmation. * @@ -247,7 +247,7 @@ class BlockchainProcessor extends AbstractProcessor { } catch(err) { Logger.error(err, `Tracker : BlockchainProcessor.onBlockHash() : error in getblockheader(${blockHash})`) } - + if(headers == null) return null @@ -263,7 +263,7 @@ class BlockchainProcessor extends AbstractProcessor { // Process the blocks return await util.seriesCall(headers, header => { - return this.processBlock(header) + return this.processBlock(header) }) } catch(e) { @@ -302,7 +302,7 @@ class BlockchainProcessor extends AbstractProcessor { } /** - * Cancel confirmation of transactions + * Cancel confirmation of transactions * and delete blocks after a given height * @param {integer} height - height of last block maintained * @returns {Promise} @@ -348,7 +348,7 @@ class BlockchainProcessor extends AbstractProcessor { Logger.info(`Tracker : Rescanning block ${height}`) const hash = await this.client.getblockhash(height) const header = await this.client.getblockheader(hash) - return this.processBlock(header) + return this.processBlock(header) } catch(e) { Logger.error(e, 'Tracker : BlockchainProcessor.rescan()') throw e @@ -367,7 +367,7 @@ class BlockchainProcessor extends AbstractProcessor { const hex = await this.client.getblock(header.hash, false) const block = new Block(hex, header) - + const txsForBroadcast = await block.checkBlock() // Send notifications From 796d8525cfe7426c2d181cfcae306c53702415c2 Mon Sep 17 00:00:00 2001 From: kenshin-samourai Date: Tue, 23 Feb 2021 18:10:56 +0100 Subject: [PATCH 54/54] update release notes --- RELEASES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/RELEASES.md b/RELEASES.md index f41079b..5078ee5 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -130,6 +130,7 @@ Upgrade to addrindexrs v0.4.0 - [#mr192](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/192) return exit code 1 instead of 2 for aborted install & upgrade - [#mr193](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/193) reactivate tor v2 hidden service for bitcoind - [#mr194](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/194) upgrade tor to v0.4.4.7 +- [#mr195](https://code.samourai.io/dojo/samourai-dojo/-/merge_requests/195) bump block height defining ibd mode - [9fe22a35](https://code.samourai.io/dojo/samourai-dojo/-/commit/9fe22a356625e0c1aeb18691d617af9118990c84) update .gitignore