mirror of
https://github.com/cryptoadvance/specter-desktop.git
synced 2026-08-13 12:33:29 +02:00
Feature: Setup Bitcoin Core from Specter (#1007)
* Package bitcoind with specter WIP * fixes * Setup wizard * Fix non default datadir * Update bitcoind.py * Data folder config * UI and text fixes * Tor setup (macOS) * Tor setup Windows and Linux support * Wizard! * action btns * About page update * Tor controller * Update specter.py * tor_controller getter * Fix wizard bug * Fix wallet json export backup * remove docker dependency for main * refactoring docker-requirement out of the way * fix import * Fix bitcoind docker issue * Update tor_daemon.py * latest pruned node * Tor options and fixes * Fix bitcoind for wondows * Windows process stop fixes * Moar windows fix attempts * Fix exit windows * Update src/cryptoadvance/specter/cli/cli_server.py Co-authored-by: Kim Neunert <kneunert@gmail.com> * Cleanups * Cleanups * Add Bitcoin Core Tor support * Update specter bitcoind and tord * Small UI update Co-authored-by: Kim Neunert <kneunert@gmail.com> Co-authored-by: Kim Neunert <k9ert@gmx.de>
This commit is contained in:
parent
a4210a33b4
commit
c00e41f764
34 changed files with 2828 additions and 534 deletions
|
|
@ -67,18 +67,6 @@ let webPreferences = {
|
|||
|
||||
app.commandLine.appendSwitch('ignore-certificate-errors');
|
||||
|
||||
try {
|
||||
require(
|
||||
'@deadcanaries/granax')({}, {
|
||||
'SocksPort': 9050,
|
||||
'ControlPort': 9051
|
||||
}).on('error', function () {
|
||||
// Tor is probably running, ignore...
|
||||
})
|
||||
} catch(e) {
|
||||
// Tor is probably running, ignore...
|
||||
}
|
||||
|
||||
let platformName = ''
|
||||
switch (process.platform) {
|
||||
case 'darwin':
|
||||
|
|
@ -181,8 +169,13 @@ function initMainWindow(specterURL) {
|
|||
});
|
||||
|
||||
mainWindow.on('close', function (event) {
|
||||
event.preventDefault();
|
||||
mainWindow.hide();
|
||||
if(platformName == 'win64') {
|
||||
quitSpecterd()
|
||||
app.quit()
|
||||
} else {
|
||||
event.preventDefault();
|
||||
mainWindow.hide();
|
||||
}
|
||||
});
|
||||
|
||||
mainWindow.webContents.on("did-fail-load", function() {
|
||||
|
|
@ -276,9 +269,11 @@ function startSpecterd(specterdPath) {
|
|||
specterdArgs = specterdArgs.concat(specterdExtraArgs)
|
||||
}
|
||||
specterdProcess = spawn(specterdPath, specterdArgs);
|
||||
specterdProcess.stdout.on('data', (_) => {
|
||||
if (mainWindow) {
|
||||
createWindow(appSettings.specterURL)
|
||||
specterdProcess.stdout.on('data', (data) => {
|
||||
if(data.toString().includes('Serving Flask app "cryptoadvance.specter.server"')) {
|
||||
if (mainWindow) {
|
||||
createWindow(appSettings.specterURL)
|
||||
}
|
||||
}
|
||||
});
|
||||
specterdProcess.stderr.on('data', function(_) {
|
||||
|
|
@ -298,6 +293,13 @@ function startSpecterd(specterdPath) {
|
|||
});
|
||||
}
|
||||
|
||||
app.on('window-all-closed', function(){
|
||||
if(platformName == 'win64') {
|
||||
quitSpecterd()
|
||||
app.quit()
|
||||
}
|
||||
});
|
||||
|
||||
app.on('before-quit', () => {
|
||||
if (!quitted) {
|
||||
quitted = true
|
||||
|
|
@ -337,7 +339,8 @@ function quitSpecterd() {
|
|||
if (specterdProcess) {
|
||||
try {
|
||||
if (platformName == 'win64') {
|
||||
exec('taskkill -F -T -PID ' + specterdProcess.pid);
|
||||
exec('taskkill /F /T /PID ' + specterdProcess.pid);
|
||||
exec('taskkill /IM specterd.exe ');
|
||||
process.kill(-specterdProcess.pid)
|
||||
}
|
||||
specterdProcess.kill('SIGINT')
|
||||
|
|
|
|||
284
pyinstaller/electron/package-lock.json
generated
284
pyinstaller/electron/package-lock.json
generated
|
|
@ -4,44 +4,12 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"7zip": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/7zip/-/7zip-0.0.6.tgz",
|
||||
"integrity": "sha1-nK+xca+CMpSQNTtIFvAzR6oVCjA=",
|
||||
"optional": true
|
||||
},
|
||||
"7zip-bin": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.0.3.tgz",
|
||||
"integrity": "sha512-GLyWIFBbGvpKPGo55JyRZAo4lVbnBiD52cKlw/0Vt+wnmKvWJkpZvsjVoaIolyBXDeAQKSicRtqFNPem9w0WYA==",
|
||||
"dev": true
|
||||
},
|
||||
"@deadcanaries/granax": {
|
||||
"version": "3.2.5",
|
||||
"resolved": "https://registry.npmjs.org/@deadcanaries/granax/-/granax-3.2.5.tgz",
|
||||
"integrity": "sha512-umsEYedNQWLSlkDnGynR8bY0pVWq2hKN1JLtV9Jit7srYHqdo+XIEsUNejq1nNBiX83WhUZVe4gOqvg8ovdMMQ==",
|
||||
"requires": {
|
||||
"7zip": "0.0.6",
|
||||
"async": "^2.3.0",
|
||||
"latest-torbrowser-version": "^2.0.2",
|
||||
"merge": "^1.2.1",
|
||||
"mkdirp": "^0.5.1",
|
||||
"mv": "^2.1.1",
|
||||
"ncp": "^2.0.0",
|
||||
"progress": "^2.0.3",
|
||||
"rimraf": "^2.6.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"@develar/schema-utils": {
|
||||
"version": "2.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
|
||||
|
|
@ -336,7 +304,8 @@
|
|||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"bcrypt-pbkdf": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -361,11 +330,6 @@
|
|||
"bluebird": "^3.5.5"
|
||||
}
|
||||
},
|
||||
"boolbase": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz",
|
||||
"integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24="
|
||||
},
|
||||
"boolean": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/boolean/-/boolean-3.0.2.tgz",
|
||||
|
|
@ -411,6 +375,7 @@
|
|||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "^1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
|
|
@ -551,19 +516,6 @@
|
|||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"cheerio": {
|
||||
"version": "1.0.0-rc.3",
|
||||
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.0.0-rc.3.tgz",
|
||||
"integrity": "sha512-0td5ijfUPuubwLUu0OBoe98gZj8C/AA+RW3v67GPlGOrvxWjZmBXiBCRU+I8VEiNyJzjth40POfHiz2RB3gImA==",
|
||||
"requires": {
|
||||
"css-select": "~1.2.0",
|
||||
"dom-serializer": "~0.1.1",
|
||||
"entities": "~1.1.1",
|
||||
"htmlparser2": "^3.9.1",
|
||||
"lodash": "^4.15.0",
|
||||
"parse5": "^3.0.1"
|
||||
}
|
||||
},
|
||||
"chromium-pickle-js": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
|
||||
|
|
@ -652,7 +604,8 @@
|
|||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"concat-stream": {
|
||||
"version": "1.6.2",
|
||||
|
|
@ -709,22 +662,6 @@
|
|||
"integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
|
||||
"dev": true
|
||||
},
|
||||
"css-select": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-1.2.0.tgz",
|
||||
"integrity": "sha1-KzoRBTnFNV8c2NMUYj6HCxIeyFg=",
|
||||
"requires": {
|
||||
"boolbase": "~1.0.0",
|
||||
"css-what": "2.1",
|
||||
"domutils": "1.5.1",
|
||||
"nth-check": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"css-what": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-2.1.3.tgz",
|
||||
"integrity": "sha512-a+EPoD+uZiNfh+5fxw2nO9QwFa6nJe2Or35fGY6Ipw1R3R4AGz1d1TEZrCegvw2YTmZ0jXirGYlzxxpYSHwpEg=="
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
|
|
@ -828,37 +765,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"dom-serializer": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
|
||||
"integrity": "sha512-l0IU0pPzLWSHBcieZbpOKgkIn3ts3vAh7ZuFyXNwJxJXk/c4Gwj9xaTJwIDVQCXawWD0qb3IzMGH5rglQaO0XA==",
|
||||
"requires": {
|
||||
"domelementtype": "^1.3.0",
|
||||
"entities": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"domelementtype": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
|
||||
"integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
|
||||
},
|
||||
"domhandler": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.4.2.tgz",
|
||||
"integrity": "sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA==",
|
||||
"requires": {
|
||||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"domutils": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
|
||||
"integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
|
||||
"requires": {
|
||||
"dom-serializer": "0",
|
||||
"domelementtype": "1"
|
||||
}
|
||||
},
|
||||
"dot-prop": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
|
||||
|
|
@ -1093,11 +999,6 @@
|
|||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"entities": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
|
||||
"integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w=="
|
||||
},
|
||||
"env-paths": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.0.tgz",
|
||||
|
|
@ -1226,11 +1127,6 @@
|
|||
"trim-repeated": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.13.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.13.0.tgz",
|
||||
"integrity": "sha512-aq6gF1BEKje4a9i9+5jimNFIpq4Q1WiwBToeRK5NvZBd/TRsmW8BsJfOEGkr76TbOyPVD3OVDN910EcUNtRYEA=="
|
||||
},
|
||||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
|
|
@ -1257,11 +1153,6 @@
|
|||
"universalify": "^0.1.0"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
|
||||
|
|
@ -1285,18 +1176,6 @@
|
|||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"glob": {
|
||||
"version": "6.0.4",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-6.0.4.tgz",
|
||||
"integrity": "sha1-DwiGD2oVUSey+t1PnOJLGqtuTSI=",
|
||||
"requires": {
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "2 || 3",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"global-agent": {
|
||||
"version": "2.1.12",
|
||||
"resolved": "https://registry.npmjs.org/global-agent/-/global-agent-2.1.12.tgz",
|
||||
|
|
@ -1405,31 +1284,6 @@
|
|||
"lru-cache": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"htmlparser2": {
|
||||
"version": "3.10.1",
|
||||
"resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.10.1.tgz",
|
||||
"integrity": "sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==",
|
||||
"requires": {
|
||||
"domelementtype": "^1.3.1",
|
||||
"domhandler": "^2.3.0",
|
||||
"domutils": "^1.5.1",
|
||||
"entities": "^1.1.1",
|
||||
"inherits": "^2.0.1",
|
||||
"readable-stream": "^3.1.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
|
||||
"integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
|
||||
"requires": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"http-cache-semantics": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz",
|
||||
|
|
@ -1467,19 +1321,11 @@
|
|||
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
|
||||
"requires": {
|
||||
"once": "^1.3.0",
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"ini": {
|
||||
"version": "1.3.8",
|
||||
|
|
@ -1707,32 +1553,6 @@
|
|||
"json-buffer": "3.0.0"
|
||||
}
|
||||
},
|
||||
"latest-torbrowser-version": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/latest-torbrowser-version/-/latest-torbrowser-version-2.0.3.tgz",
|
||||
"integrity": "sha512-+6fkERFLxo2QiNn7x0txjEF199tG4CGVI9X101d7IuZAapO/hGOC+nHD9BEPNNaMqmvIUkT/o3CdZ+NS5wX/iQ==",
|
||||
"requires": {
|
||||
"async": "^2.6.0",
|
||||
"cheerio": "^1.0.0-rc.2",
|
||||
"follow-redirects": "^1.2.4",
|
||||
"semver": "^5.4.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "2.6.3",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz",
|
||||
"integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.14"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.7.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
|
||||
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"latest-version": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz",
|
||||
|
|
@ -1751,7 +1571,9 @@
|
|||
"lodash": {
|
||||
"version": "4.17.20",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
|
||||
"integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
},
|
||||
"lowercase-keys": {
|
||||
"version": "1.0.1",
|
||||
|
|
@ -1804,11 +1626,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"merge": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/merge/-/merge-1.2.1.tgz",
|
||||
"integrity": "sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ=="
|
||||
},
|
||||
"mime": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/mime/-/mime-2.4.6.tgz",
|
||||
|
|
@ -1838,6 +1655,7 @@
|
|||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "^1.1.7"
|
||||
}
|
||||
|
|
@ -1845,12 +1663,14 @@
|
|||
"minimist": {
|
||||
"version": "1.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.5",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
|
||||
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "^1.2.5"
|
||||
}
|
||||
|
|
@ -1865,31 +1685,6 @@
|
|||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"mv": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/mv/-/mv-2.1.1.tgz",
|
||||
"integrity": "sha1-rmzg1vbV4KT32JN5jQPB6pVZtqI=",
|
||||
"requires": {
|
||||
"mkdirp": "~0.5.1",
|
||||
"ncp": "~2.0.0",
|
||||
"rimraf": "~2.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"rimraf": {
|
||||
"version": "2.4.5",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.4.5.tgz",
|
||||
"integrity": "sha1-7nEM5dk6j9uFb7Xqj/Di11k0sto=",
|
||||
"requires": {
|
||||
"glob": "^6.0.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ncp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz",
|
||||
"integrity": "sha1-GVoh1sRuNh0vsSgbo4uR6d9727M="
|
||||
},
|
||||
"normalize-package-data": {
|
||||
"version": "2.5.0",
|
||||
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
|
||||
|
|
@ -1933,14 +1728,6 @@
|
|||
"pify": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"nth-check": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-1.0.2.tgz",
|
||||
"integrity": "sha512-WeBOdju8SnzPN5vTUJYxYUxLeXpCaVP5i5e0LF8fg7WORF2Wd7wFX/pk0tYZk7s8T+J7VLy0Da6J1+wCT0AtHg==",
|
||||
"requires": {
|
||||
"boolbase": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
|
|
@ -1987,24 +1774,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"parse5": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.3.tgz",
|
||||
"integrity": "sha512-rgO9Zg5LLLkfJF9E6CCmXlSE4UVceloys8JrFqCcHloC3usd/kJCyPDwH2SOlzix2j3xaP9sUX3e8+kvkuleAA==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"path-exists": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
|
||||
},
|
||||
"path-parse": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
|
|
@ -2043,7 +1817,8 @@
|
|||
"progress": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
|
||||
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
|
||||
"dev": true
|
||||
},
|
||||
"proto-list": {
|
||||
"version": "1.2.4",
|
||||
|
|
@ -2193,29 +1968,6 @@
|
|||
"lowercase-keys": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"dependencies": {
|
||||
"glob": {
|
||||
"version": "7.1.6",
|
||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||
"requires": {
|
||||
"fs.realpath": "^1.0.0",
|
||||
"inflight": "^1.0.4",
|
||||
"inherits": "2",
|
||||
"minimatch": "^3.0.4",
|
||||
"once": "^1.3.0",
|
||||
"path-is-absolute": "^1.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"roarr": {
|
||||
"version": "2.15.4",
|
||||
"resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
|
||||
|
|
@ -2451,6 +2203,7 @@
|
|||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
|
|
@ -2671,7 +2424,8 @@
|
|||
"util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
|
||||
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
|
||||
"dev": true
|
||||
},
|
||||
"uuid": {
|
||||
"version": "3.4.0",
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@deadcanaries/granax": "^3.2.5",
|
||||
"electron-context-menu": "^2.3.0",
|
||||
"electron-default-menu": "^1.0.2",
|
||||
"extract-zip": "^2.0.1",
|
||||
|
|
|
|||
|
|
@ -18,3 +18,4 @@ embit==0.1.2
|
|||
psutil==5.7.3
|
||||
pyopenssl==20.0.1
|
||||
flask_wtf==0.14.3
|
||||
pgpy==0.5.3
|
||||
|
|
|
|||
|
|
@ -87,6 +87,7 @@ cryptography==3.3.2 \
|
|||
--hash=sha256:e18e6ab84dfb0ab997faf8cca25a86ff15dfea4027b986322026cc99e0a892da
|
||||
# via
|
||||
# noiseprotocol
|
||||
# pgpy
|
||||
# pyopenssl
|
||||
daemonize==2.5.0 \
|
||||
--hash=sha256:9b6b91311a9d934ff3f5f766666635ca280d3de8e7137e4cd7d3f052543b989f \
|
||||
|
|
@ -207,6 +208,10 @@ noiseprotocol==0.3.1 \
|
|||
pbkdf2==1.3 \
|
||||
--hash=sha256:ac6397369f128212c43064a2b4878038dab78dab41875364554aaf2a684e6979
|
||||
# via mnemonic
|
||||
pgpy==0.5.3 \
|
||||
--hash=sha256:a49c269cedcaf82ac6999bcae5fd3f543ecb1c759f9d48a15ad8d8fa4ac03987 \
|
||||
--hash=sha256:cba6fbbb44a896a8a4f5807b3d8d4943a8f7a6607be11587f4a27734c711c1dd
|
||||
# via -r requirements.in
|
||||
protobuf==3.13.0 \
|
||||
--hash=sha256:0bba42f439bf45c0f600c3c5993666fcb88e8441d011fad80a11df6f324eef33 \
|
||||
--hash=sha256:1e834076dfef9e585815757a2c7e4560c7ccc5962b9d09f831214c693a91b463 \
|
||||
|
|
@ -243,6 +248,10 @@ psutil==5.7.3 \
|
|||
pyaes==1.6.1 \
|
||||
--hash=sha256:02c1b1405c38d3c370b085fb952dd8bea3fadcee6411ad99f312cc129c536d8f
|
||||
# via hwi
|
||||
pyasn1==0.4.8 \
|
||||
--hash=sha256:39c7e2ec30515947ff4e87fb6f456dfc6e84857d34be479c9d4a4ba4bf46aa5d \
|
||||
--hash=sha256:aef77c9fb94a3ac588e87841208bdec464471d9871bd5050a287cc9a475cd0ba
|
||||
# via pgpy
|
||||
pycparser==2.20 \
|
||||
--hash=sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0 \
|
||||
--hash=sha256:7582ad22678f0fcd81102833f60ef8d0e57288b6b5fb00323d101be910e35705
|
||||
|
|
@ -279,6 +288,7 @@ six==1.12.0 \
|
|||
# -r requirements.in
|
||||
# cryptography
|
||||
# flask-cors
|
||||
# pgpy
|
||||
# protobuf
|
||||
# pyopenssl
|
||||
stem==1.8.0 \
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
""" Stuff to control a bitcoind-instance. Either directly by access to a bitcoind-executable or
|
||||
via docker.
|
||||
""" Stuff to control a bitcoind-instance.
|
||||
"""
|
||||
import atexit
|
||||
import logging
|
||||
|
|
@ -11,8 +10,8 @@ import subprocess
|
|||
import tempfile
|
||||
import time
|
||||
import json
|
||||
import platform
|
||||
|
||||
import docker
|
||||
|
||||
from .util.shell import which
|
||||
from .rpc import RpcError
|
||||
|
|
@ -80,8 +79,13 @@ class Btcd_conn:
|
|||
class BitcoindController:
|
||||
""" A kind of abstract class to simplify running a bitcoind with or without docker """
|
||||
|
||||
def __init__(self, rpcport=18443):
|
||||
self.rpcconn = Btcd_conn(rpcport=rpcport)
|
||||
def __init__(
|
||||
self, rpcport=18443, network="regtest", rpcuser="bitcoin", rpcpassword="secret"
|
||||
):
|
||||
self.rpcconn = Btcd_conn(
|
||||
rpcuser=rpcuser, rpcpassword=rpcpassword, rpcport=rpcport
|
||||
)
|
||||
self.network = network
|
||||
|
||||
def start_bitcoind(
|
||||
self, cleanup_at_exit=False, cleanup_hard=False, datadir=None, extra_args=[]
|
||||
|
|
@ -103,7 +107,8 @@ class BitcoindController:
|
|||
)
|
||||
|
||||
self.wait_for_bitcoind(self.rpcconn)
|
||||
self.mine(block_count=100)
|
||||
if self.network == "regtest":
|
||||
self.mine(block_count=100)
|
||||
return self.rpcconn
|
||||
|
||||
def version(self):
|
||||
|
|
@ -160,23 +165,24 @@ class BitcoindController:
|
|||
try:
|
||||
rpcconn.get_rpc() # that call will also check the connection
|
||||
return True
|
||||
except ConnectionRefusedError:
|
||||
except ConnectionRefusedError as e:
|
||||
return False
|
||||
except TypeError:
|
||||
except TypeError as e:
|
||||
return False
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
print(f"couldn't reach bitcoind - message returned: {e}")
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def wait_for_bitcoind(rpcconn):
|
||||
""" tries to reach the bitcoind via rpc. Will timeout after 10 seconds """
|
||||
""" tries to reach the bitcoind via rpc. Will timeout after 30 seconds """
|
||||
i = 0
|
||||
while True:
|
||||
if BitcoindController.check_bitcoind(rpcconn):
|
||||
break
|
||||
time.sleep(0.5)
|
||||
i = i + 1
|
||||
if i > 20:
|
||||
if i > 60:
|
||||
raise Exception(
|
||||
"Timeout while trying to reach bitcoind at rpcport {} !".format(
|
||||
rpcconn
|
||||
|
|
@ -197,11 +203,13 @@ class BitcoindController:
|
|||
run_docker=True,
|
||||
datadir=None,
|
||||
bitcoind_path="bitcoind",
|
||||
network="regtest",
|
||||
extra_args=[],
|
||||
):
|
||||
""" returns a bitcoind-command to run bitcoind """
|
||||
btcd_cmd = "{} ".format(bitcoind_path)
|
||||
btcd_cmd += " -regtest "
|
||||
btcd_cmd = '"{}" '.format(bitcoind_path)
|
||||
if network != "mainnet":
|
||||
btcd_cmd += " -{} ".format(network)
|
||||
btcd_cmd += " -fallbackfee=0.0002 "
|
||||
btcd_cmd += " -port={} -rpcport={} -rpcbind=0.0.0.0 -rpcbind=0.0.0.0".format(
|
||||
rpcconn.rpcport - 1, rpcconn.rpcport
|
||||
|
|
@ -214,7 +222,7 @@ class BitcoindController:
|
|||
btcd_cmd += " -noprinttoconsole"
|
||||
if datadir == None:
|
||||
datadir = tempfile.mkdtemp(prefix="bitcoind_datadir")
|
||||
btcd_cmd += " -datadir={} ".format(datadir)
|
||||
btcd_cmd += ' -datadir="{}" '.format(datadir)
|
||||
if extra_args:
|
||||
btcd_cmd += " {}".format(" ".join(extra_args))
|
||||
logger.debug("constructed bitcoind-command: %s", btcd_cmd)
|
||||
|
|
@ -222,10 +230,19 @@ class BitcoindController:
|
|||
|
||||
|
||||
class BitcoindPlainController(BitcoindController):
|
||||
""" A class controlling the bicoind-process directly on the machine """
|
||||
""" A class controlling the bitcoind-process directly on the machine """
|
||||
|
||||
def __init__(self, bitcoind_path="bitcoind", rpcport=18443):
|
||||
super().__init__(rpcport=rpcport)
|
||||
def __init__(
|
||||
self,
|
||||
bitcoind_path="bitcoind",
|
||||
rpcport=18443,
|
||||
network="regtest",
|
||||
rpcuser="bitcoin",
|
||||
rpcpassword="secret",
|
||||
):
|
||||
super().__init__(
|
||||
rpcport=rpcport, network=network, rpcuser=rpcuser, rpcpassword=rpcpassword
|
||||
)
|
||||
self.bitcoind_path = bitcoind_path
|
||||
self.rpcconn.ipaddress = "localhost"
|
||||
|
||||
|
|
@ -240,11 +257,15 @@ class BitcoindPlainController(BitcoindController):
|
|||
run_docker=False,
|
||||
datadir=datadir,
|
||||
bitcoind_path=self.bitcoind_path,
|
||||
network=self.network,
|
||||
extra_args=extra_args,
|
||||
)
|
||||
logger.debug("About to execute: {}".format(bitcoind_cmd))
|
||||
# exec will prevent creating a child-process and will make bitcoind_proc.terminate() work as expected
|
||||
self.bitcoind_proc = subprocess.Popen("exec " + bitcoind_cmd, shell=True)
|
||||
self.bitcoind_proc = subprocess.Popen(
|
||||
("exec " if platform.system() != "Windows" else "") + bitcoind_cmd,
|
||||
shell=True,
|
||||
)
|
||||
logger.debug(
|
||||
"Running bitcoind-process with pid {}".format(self.bitcoind_proc.pid)
|
||||
)
|
||||
|
|
@ -278,6 +299,8 @@ class BitcoindPlainController(BitcoindController):
|
|||
for p in alive:
|
||||
logger.info("bitcoind did not terminated in time, killing!")
|
||||
p.kill()
|
||||
if platform.system() == "Windows":
|
||||
subprocess.run("Taskkill /IM bitcoind.exe /F")
|
||||
|
||||
def stop_bitcoind(self):
|
||||
self.cleanup_bitcoind()
|
||||
|
|
@ -292,187 +315,6 @@ class BitcoindPlainController(BitcoindController):
|
|||
return True
|
||||
|
||||
|
||||
class BitcoindDockerController(BitcoindController):
|
||||
""" A class specifically controlling a docker-based bitcoind-container """
|
||||
|
||||
def __init__(self, rpcport=18443, docker_tag="latest"):
|
||||
self.btcd_container = None
|
||||
super().__init__(rpcport=rpcport)
|
||||
self.docker_tag = docker_tag
|
||||
|
||||
if self.detect_bitcoind_container(rpcport) != None:
|
||||
rpcconn, btcd_container = self.detect_bitcoind_container(rpcport)
|
||||
logger.debug("Detected old container ... deleting it")
|
||||
btcd_container.stop()
|
||||
btcd_container.remove()
|
||||
|
||||
def _start_bitcoind(
|
||||
self, cleanup_at_exit, cleanup_hard=False, datadir=None, extra_args=[]
|
||||
):
|
||||
if datadir != None:
|
||||
# ignored
|
||||
pass
|
||||
bitcoind_path = self.construct_bitcoind_cmd(self.rpcconn, extra_args=extra_args)
|
||||
dclient = docker.from_env()
|
||||
logger.debug("Running (in docker): {}".format(bitcoind_path))
|
||||
ports = {
|
||||
"{}/tcp".format(self.rpcconn.rpcport - 1): self.rpcconn.rpcport - 1,
|
||||
"{}/tcp".format(self.rpcconn.rpcport): self.rpcconn.rpcport,
|
||||
}
|
||||
logger.debug("portmapping: {}".format(ports))
|
||||
image = dclient.images.get(
|
||||
"registry.gitlab.com/cryptoadvance/specter-desktop/python-bitcoind:{}".format(
|
||||
self.docker_tag
|
||||
)
|
||||
)
|
||||
self.btcd_container = dclient.containers.run(
|
||||
image,
|
||||
bitcoind_path,
|
||||
ports=ports,
|
||||
detach=True,
|
||||
)
|
||||
|
||||
def cleanup_docker_bitcoind(*args):
|
||||
logger.info("Cleaning up bitcoind-docker-container")
|
||||
self.btcd_container.stop()
|
||||
self.btcd_container.remove()
|
||||
|
||||
if cleanup_at_exit:
|
||||
logger.debug(
|
||||
"Register function cleanup_docker_bitcoind for SIGINT and SIGTERM"
|
||||
)
|
||||
# This is for CTRL-C --> SIGINT
|
||||
signal.signal(signal.SIGINT, cleanup_docker_bitcoind)
|
||||
# This is for kill $pid --> SIGTERM
|
||||
signal.signal(signal.SIGTERM, cleanup_docker_bitcoind)
|
||||
|
||||
logger.debug(
|
||||
"Waiting for container {} to come up".format(self.btcd_container.id)
|
||||
)
|
||||
self.wait_for_container()
|
||||
rpcconn, _ = self.detect_bitcoind_container(self.rpcconn.rpcport)
|
||||
if rpcconn == None:
|
||||
raise Exception(
|
||||
"Couldn't find container or it died already. Check the logs!"
|
||||
)
|
||||
else:
|
||||
self.rpcconn = rpcconn
|
||||
return
|
||||
|
||||
def stop_bitcoind(self):
|
||||
if self.btcd_container != None:
|
||||
self.btcd_container.reload()
|
||||
if self.btcd_container.status == "running":
|
||||
_, container = self.detect_bitcoind_container(self.rpcconn.rpcport)
|
||||
if container == self.btcd_container:
|
||||
self.btcd_container.stop()
|
||||
logger.info("Stopped btcd_container {}".format(self.btcd_container))
|
||||
self.btcd_container.remove()
|
||||
return
|
||||
raise Exception("Ambigious Container running")
|
||||
|
||||
def check_existing(self):
|
||||
""" Checks whether self.btcd_container is up2date and not ambigious """
|
||||
if self.btcd_container != None:
|
||||
self.btcd_container.reload()
|
||||
if self.btcd_container.status == "running":
|
||||
rpcconn, container = self.detect_bitcoind_container(
|
||||
self.rpcconn.rpcport
|
||||
)
|
||||
if container == self.btcd_container:
|
||||
return rpcconn
|
||||
raise Exception("Ambigious Container running")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def search_bitcoind_container(all=False):
|
||||
""" returns a list of containers which are running bitcoind """
|
||||
d_client = docker.from_env()
|
||||
return [
|
||||
c
|
||||
for c in d_client.containers.list(all)
|
||||
if (c.attrs["Config"].get("Cmd") or [""])[0] == "bitcoind"
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def detect_bitcoind_container(with_rpcport):
|
||||
"""checks all the containers for a bitcoind one, parses the arguments and initializes
|
||||
the object accordingly
|
||||
returns rpcconn, btcd_container
|
||||
"""
|
||||
d_client = docker.from_env()
|
||||
potential_btcd_containers = BitcoindDockerController.search_bitcoind_container()
|
||||
if len(potential_btcd_containers) == 0:
|
||||
logger.debug(
|
||||
"could not detect container. Candidates: {}".format(
|
||||
d_client.containers.list()
|
||||
)
|
||||
)
|
||||
all_candidates = BitcoindDockerController.search_bitcoind_container(
|
||||
all=True
|
||||
)
|
||||
logger.debug(
|
||||
"could not detect container. All Candidates: {}".format(all_candidates)
|
||||
)
|
||||
if len(all_candidates) > 0:
|
||||
logger.debug("100 chars of logs of first candidate")
|
||||
logger.debug(all_candidates[0].logs()[0:100])
|
||||
return None
|
||||
for btcd_container in potential_btcd_containers:
|
||||
rpcport = int(
|
||||
[
|
||||
arg
|
||||
for arg in btcd_container.attrs["Config"]["Cmd"]
|
||||
if "rpcport" in arg
|
||||
][0].split("=")[1]
|
||||
)
|
||||
if rpcport != with_rpcport:
|
||||
logger.debug(
|
||||
"checking port {} against searched port {}".format(
|
||||
type(rpcport), type(with_rpcport)
|
||||
)
|
||||
)
|
||||
continue
|
||||
rpcpassword = [
|
||||
arg
|
||||
for arg in btcd_container.attrs["Config"]["Cmd"]
|
||||
if "rpcpassword" in arg
|
||||
][0].split("=")[1]
|
||||
rpcuser = [
|
||||
arg for arg in btcd_container.attrs["Config"]["Cmd"] if "rpcuser" in arg
|
||||
][0].split("=")[1]
|
||||
if "CI" in os.environ: # this is a predefined variable in gitlab
|
||||
# This works on Linux (direct docker) and gitlab-CI but not on MAC
|
||||
ipaddress = btcd_container.attrs["NetworkSettings"]["IPAddress"]
|
||||
else:
|
||||
# This works on most machines but not on gitlab-CI
|
||||
ipaddress = "127.0.0.1"
|
||||
rpcconn = Btcd_conn(
|
||||
rpcuser=rpcuser,
|
||||
rpcpassword=rpcpassword,
|
||||
rpcport=rpcport,
|
||||
ipaddress=ipaddress,
|
||||
)
|
||||
logger.info("detected container {}".format(btcd_container.id))
|
||||
return rpcconn, btcd_container
|
||||
logger.debug("No matching container found")
|
||||
return None
|
||||
|
||||
def wait_for_container(self):
|
||||
""" waits for the docker-container to come up. Times out after 10 seconds """
|
||||
i = 0
|
||||
while True:
|
||||
ip_address = self.btcd_container.attrs["NetworkSettings"]["IPAddress"]
|
||||
if ip_address.startswith("172"):
|
||||
self.rpcconn.ipaddress = ip_address
|
||||
break
|
||||
self.btcd_container.reload()
|
||||
time.sleep(0.5)
|
||||
i = i + 1
|
||||
if i > 20:
|
||||
raise Exception("Timeout while starting bitcoind-docker-container!")
|
||||
|
||||
|
||||
def fetch_wallet_addresses_for_mining(data_folder):
|
||||
"""parses all the wallet-jsons in the folder (default ~/.specter/wallets/regtest)
|
||||
and returns an array with the addresses
|
||||
|
|
|
|||
193
src/cryptoadvance/specter/bitcoind_docker.py
Normal file
193
src/cryptoadvance/specter/bitcoind_docker.py
Normal file
|
|
@ -0,0 +1,193 @@
|
|||
import logging
|
||||
|
||||
# the docker-dependency is special as it's only used with pytest --docker or
|
||||
# if you manually start bitcoind --docker
|
||||
# If this fails, you need to pip install the test-requirements
|
||||
import docker
|
||||
import os
|
||||
from .bitcoind import BitcoindController, Btcd_conn
|
||||
import signal
|
||||
import time
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BitcoindDockerController(BitcoindController):
|
||||
""" A class specifically controlling a docker-based bitcoind-container """
|
||||
|
||||
def __init__(self, rpcport=18443, docker_tag="latest"):
|
||||
self.btcd_container = None
|
||||
super().__init__(rpcport=rpcport)
|
||||
self.docker_tag = docker_tag
|
||||
|
||||
if self.detect_bitcoind_container(rpcport) != None:
|
||||
rpcconn, btcd_container = self.detect_bitcoind_container(rpcport)
|
||||
logger.debug("Detected old container ... deleting it")
|
||||
btcd_container.stop()
|
||||
btcd_container.remove()
|
||||
|
||||
def _start_bitcoind(
|
||||
self, cleanup_at_exit, cleanup_hard=False, datadir=None, extra_args=[]
|
||||
):
|
||||
if datadir != None:
|
||||
# ignored
|
||||
pass
|
||||
bitcoind_path = self.construct_bitcoind_cmd(self.rpcconn, extra_args=extra_args)
|
||||
dclient = docker.from_env()
|
||||
logger.debug("Running (in docker): {}".format(bitcoind_path))
|
||||
ports = {
|
||||
"{}/tcp".format(self.rpcconn.rpcport - 1): self.rpcconn.rpcport - 1,
|
||||
"{}/tcp".format(self.rpcconn.rpcport): self.rpcconn.rpcport,
|
||||
}
|
||||
logger.debug("portmapping: {}".format(ports))
|
||||
image = dclient.images.get(
|
||||
"registry.gitlab.com/cryptoadvance/specter-desktop/python-bitcoind:{}".format(
|
||||
self.docker_tag
|
||||
)
|
||||
)
|
||||
self.btcd_container = dclient.containers.run(
|
||||
image,
|
||||
bitcoind_path,
|
||||
ports=ports,
|
||||
detach=True,
|
||||
)
|
||||
|
||||
def cleanup_docker_bitcoind(*args):
|
||||
logger.info("Cleaning up bitcoind-docker-container")
|
||||
self.btcd_container.stop()
|
||||
self.btcd_container.remove()
|
||||
|
||||
if cleanup_at_exit:
|
||||
logger.debug(
|
||||
"Register function cleanup_docker_bitcoind for SIGINT and SIGTERM"
|
||||
)
|
||||
# This is for CTRL-C --> SIGINT
|
||||
signal.signal(signal.SIGINT, cleanup_docker_bitcoind)
|
||||
# This is for kill $pid --> SIGTERM
|
||||
signal.signal(signal.SIGTERM, cleanup_docker_bitcoind)
|
||||
|
||||
logger.debug(
|
||||
"Waiting for container {} to come up".format(self.btcd_container.id)
|
||||
)
|
||||
self.wait_for_container()
|
||||
rpcconn, _ = self.detect_bitcoind_container(self.rpcconn.rpcport)
|
||||
if rpcconn == None:
|
||||
raise Exception(
|
||||
"Couldn't find container or it died already. Check the logs!"
|
||||
)
|
||||
else:
|
||||
self.rpcconn = rpcconn
|
||||
return
|
||||
|
||||
def stop_bitcoind(self):
|
||||
if self.btcd_container != None:
|
||||
self.btcd_container.reload()
|
||||
if self.btcd_container.status == "running":
|
||||
_, container = self.detect_bitcoind_container(self.rpcconn.rpcport)
|
||||
if container == self.btcd_container:
|
||||
self.btcd_container.stop()
|
||||
logger.info("Stopped btcd_container {}".format(self.btcd_container))
|
||||
self.btcd_container.remove()
|
||||
return
|
||||
raise Exception("Ambigious Container running")
|
||||
|
||||
def check_existing(self):
|
||||
""" Checks whether self.btcd_container is up2date and not ambigious """
|
||||
if self.btcd_container != None:
|
||||
self.btcd_container.reload()
|
||||
if self.btcd_container.status == "running":
|
||||
rpcconn, container = self.detect_bitcoind_container(
|
||||
self.rpcconn.rpcport
|
||||
)
|
||||
if container == self.btcd_container:
|
||||
return rpcconn
|
||||
raise Exception("Ambigious Container running")
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def search_bitcoind_container(all=False):
|
||||
""" returns a list of containers which are running bitcoind """
|
||||
d_client = docker.from_env()
|
||||
return [
|
||||
c
|
||||
for c in d_client.containers.list(all)
|
||||
if (c.attrs["Config"].get("Cmd") or [""])[0] == "bitcoind"
|
||||
]
|
||||
|
||||
@staticmethod
|
||||
def detect_bitcoind_container(with_rpcport):
|
||||
"""checks all the containers for a bitcoind one, parses the arguments and initializes
|
||||
the object accordingly
|
||||
returns rpcconn, btcd_container
|
||||
"""
|
||||
d_client = docker.from_env()
|
||||
potential_btcd_containers = BitcoindDockerController.search_bitcoind_container()
|
||||
if len(potential_btcd_containers) == 0:
|
||||
logger.debug(
|
||||
"could not detect container. Candidates: {}".format(
|
||||
d_client.containers.list()
|
||||
)
|
||||
)
|
||||
all_candidates = BitcoindDockerController.search_bitcoind_container(
|
||||
all=True
|
||||
)
|
||||
logger.debug(
|
||||
"could not detect container. All Candidates: {}".format(all_candidates)
|
||||
)
|
||||
if len(all_candidates) > 0:
|
||||
logger.debug("100 chars of logs of first candidate")
|
||||
logger.debug(all_candidates[0].logs()[0:100])
|
||||
return None
|
||||
for btcd_container in potential_btcd_containers:
|
||||
rpcport = int(
|
||||
[
|
||||
arg
|
||||
for arg in btcd_container.attrs["Config"]["Cmd"]
|
||||
if "rpcport" in arg
|
||||
][0].split("=")[1]
|
||||
)
|
||||
if rpcport != with_rpcport:
|
||||
logger.debug(
|
||||
"checking port {} against searched port {}".format(
|
||||
type(rpcport), type(with_rpcport)
|
||||
)
|
||||
)
|
||||
continue
|
||||
rpcpassword = [
|
||||
arg
|
||||
for arg in btcd_container.attrs["Config"]["Cmd"]
|
||||
if "rpcpassword" in arg
|
||||
][0].split("=")[1]
|
||||
rpcuser = [
|
||||
arg for arg in btcd_container.attrs["Config"]["Cmd"] if "rpcuser" in arg
|
||||
][0].split("=")[1]
|
||||
if "CI" in os.environ: # this is a predefined variable in gitlab
|
||||
# This works on Linux (direct docker) and gitlab-CI but not on MAC
|
||||
ipaddress = btcd_container.attrs["NetworkSettings"]["IPAddress"]
|
||||
else:
|
||||
# This works on most machines but not on gitlab-CI
|
||||
ipaddress = "127.0.0.1"
|
||||
rpcconn = Btcd_conn(
|
||||
rpcuser=rpcuser,
|
||||
rpcpassword=rpcpassword,
|
||||
rpcport=rpcport,
|
||||
ipaddress=ipaddress,
|
||||
)
|
||||
logger.info("detected container {}".format(btcd_container.id))
|
||||
return rpcconn, btcd_container
|
||||
logger.debug("No matching container found")
|
||||
return None
|
||||
|
||||
def wait_for_container(self):
|
||||
""" waits for the docker-container to come up. Times out after 10 seconds """
|
||||
i = 0
|
||||
while True:
|
||||
ip_address = self.btcd_container.attrs["NetworkSettings"]["IPAddress"]
|
||||
if ip_address.startswith("172"):
|
||||
self.rpcconn.ipaddress = ip_address
|
||||
break
|
||||
self.btcd_container.reload()
|
||||
time.sleep(0.5)
|
||||
i = i + 1
|
||||
if i > 20:
|
||||
raise Exception("Timeout while starting bitcoind-docker-container!")
|
||||
|
|
@ -98,7 +98,7 @@ def bitcoind(
|
|||
# In order to avoid these dependencies for production use, we're importing them here:
|
||||
import docker
|
||||
|
||||
from ..bitcoind import BitcoindDockerController, BitcoindPlainController
|
||||
from ..bitcoind import BitcoindPlainController
|
||||
|
||||
if config is None:
|
||||
config = DEFAULT_CONFIG
|
||||
|
|
@ -159,6 +159,8 @@ def bitcoind(
|
|||
Path(config_obj["BTCD_REGTEST_DATA_DIR"]).mkdir(parents=True, exist_ok=True)
|
||||
else:
|
||||
echo("starting container")
|
||||
from ..bitcoind_docker import BitcoindDockerController
|
||||
|
||||
my_bitcoind = BitcoindDockerController(docker_tag=docker_tag)
|
||||
try:
|
||||
my_bitcoind.start_bitcoind(
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from urllib.parse import urlparse
|
|||
|
||||
from ..server import create_app, init_app
|
||||
from ..util.tor import start_hidden_service, stop_hidden_services
|
||||
from ..specter_error import SpecterError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -197,18 +198,6 @@ def server(
|
|||
|
||||
# debug is false by default
|
||||
def run(debug=debug):
|
||||
try:
|
||||
tor_control_address = urlparse(app.specter.proxy_url).netloc.split(":")[0]
|
||||
if tor_control_address == "localhost":
|
||||
tor_control_address = "127.0.0.1"
|
||||
app.controller = Controller.from_port(
|
||||
address=tor_control_address,
|
||||
port=int(app.specter.tor_control_port)
|
||||
if app.specter.tor_control_port
|
||||
else "default",
|
||||
)
|
||||
except Exception:
|
||||
app.controller = None
|
||||
try:
|
||||
# if we have certificates
|
||||
if "ssl_context" in kwargs:
|
||||
|
|
@ -245,8 +234,12 @@ def server(
|
|||
app.run(debug=debug, **kwargs)
|
||||
stop_hidden_services(app)
|
||||
finally:
|
||||
if app.controller is not None:
|
||||
app.controller.close()
|
||||
try:
|
||||
if app.specter.tor_controller is not None:
|
||||
app.specter.tor_controller.close()
|
||||
except SpecterError as se:
|
||||
# no reason to break startup here
|
||||
logger.error("Could not initialize tor-system")
|
||||
|
||||
# check if we should run a daemon or not
|
||||
if daemon or restart:
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ class BaseConfig(object):
|
|||
SPECTER_SSL_CERT_SERIAL_NUMBER = int(
|
||||
os.getenv("SPECTER_SSL_CERT_SERIAL_NUMBER", random.randrange(1, 100000))
|
||||
)
|
||||
INTERNAL_BITCOIND_VERSION = os.getenv("INTERNAL_BITCOIND_VERSION", "0.21.0")
|
||||
|
||||
|
||||
class DevelopmentConfig(BaseConfig):
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import random, traceback, socket
|
||||
import random, traceback, socket, threading, os
|
||||
from datetime import datetime
|
||||
from binascii import unhexlify
|
||||
from flask import make_response
|
||||
|
|
@ -18,7 +18,13 @@ from ..helpers import (
|
|||
from ..specter import Specter
|
||||
from ..specter_error import SpecterError
|
||||
from ..util.tor import start_hidden_service, stop_hidden_services
|
||||
|
||||
from ..util.bitcoind_setup_tasks import (
|
||||
setup_bitcoind_thread,
|
||||
setup_bitcoind_directory_thread,
|
||||
)
|
||||
from ..util.tor_setup_tasks import (
|
||||
setup_tor_thread,
|
||||
)
|
||||
from pathlib import Path
|
||||
|
||||
env_path = Path(".") / ".flaskenv"
|
||||
|
|
@ -104,6 +110,133 @@ def about():
|
|||
return render_template("base.jinja", specter=app.specter, rand=rand)
|
||||
|
||||
|
||||
@app.route("/node_setup_wizard/", defaults={"step": 0}, methods=["GET", "POST"])
|
||||
@app.route("/node_setup_wizard/<step>", methods=["GET", "POST"])
|
||||
@login_required
|
||||
def node_setup_wizard(step):
|
||||
app.specter.config["bitcoind_setup"]["error"] = ""
|
||||
app.specter.config["bitcoind_setup"]["stage"] = ""
|
||||
app.specter.config["bitcoind_setup"]["stage_progress"] = -1
|
||||
app.specter.config["torbrowser_setup"]["error"] = ""
|
||||
app.specter.config["torbrowser_setup"]["stage"] = ""
|
||||
app.specter.config["torbrowser_setup"]["stage_progress"] = -1
|
||||
app.specter._save()
|
||||
|
||||
return render_template(
|
||||
"node_setup_wizard.jinja", step=step, specter=app.specter, rand=rand
|
||||
)
|
||||
|
||||
|
||||
@app.route("/setup_bitcoind", methods=["POST"])
|
||||
@login_required
|
||||
def setup_bitcoind():
|
||||
bitcoind_setup_status = app.specter.config.get(
|
||||
"bitcoind_setup", {"stage_progress": -1}
|
||||
)
|
||||
if (
|
||||
not os.path.isfile(app.specter.bitcoind_path)
|
||||
and bitcoind_setup_status["stage_progress"] == -1
|
||||
):
|
||||
app.specter.config["rpc"]["datadir"] = request.form.get(
|
||||
"bitcoin_core_datadir", app.specter.config["rpc"]["datadir"]
|
||||
)
|
||||
app.specter.config["bitcoind_setup"]["stage_progress"] = 0
|
||||
app.specter.config["bitcoind_setup"]["error"] = ""
|
||||
app.specter._save()
|
||||
t = threading.Thread(
|
||||
target=setup_bitcoind_thread,
|
||||
args=(app.specter, app.config["INTERNAL_BITCOIND_VERSION"]),
|
||||
)
|
||||
t.start()
|
||||
elif os.path.isfile(app.specter.bitcoind_path):
|
||||
return {"error": "bitcoind already installed"}
|
||||
elif bitcoind_setup_status["stage_progress"] != -1:
|
||||
return {"error": "bitcoind installation is still under progress"}
|
||||
return {"success": "Starting Bitcoin Core setup!"}
|
||||
|
||||
|
||||
@app.route("/setup_bitcoind_directory", methods=["POST"])
|
||||
@login_required
|
||||
def setup_bitcoind_directory():
|
||||
bitcoind_setup_status = app.specter.config.get(
|
||||
"bitcoind_setup", {"stage_progress": -1}
|
||||
)
|
||||
if (
|
||||
os.path.isfile(app.specter.bitcoind_path)
|
||||
and bitcoind_setup_status["stage_progress"] == -1
|
||||
):
|
||||
app.specter.config["bitcoind_setup"]["stage_progress"] = 0
|
||||
app.specter.config["bitcoind_setup"]["error"] = ""
|
||||
app.specter._save()
|
||||
quicksync = request.form["quicksync"] == "true"
|
||||
pruned = request.form["nodetype"] == "pruned"
|
||||
t = threading.Thread(
|
||||
target=setup_bitcoind_directory_thread,
|
||||
args=(app.specter, quicksync, pruned),
|
||||
)
|
||||
t.start()
|
||||
elif not os.path.isfile(app.specter.bitcoind_path):
|
||||
return {"error": "bitcoind in not installed but required for this step"}
|
||||
elif bitcoind_setup_status["stage_progress"] != -1:
|
||||
return {"error": "bitcoind installation is still under progress"}
|
||||
return {"success": "Starting Bitcoin Core setup!"}
|
||||
|
||||
|
||||
@app.route("/setup_tor", methods=["POST"])
|
||||
@login_required
|
||||
def setup_tor():
|
||||
tor_setup_status = app.specter.config.get(
|
||||
"torbrowser_setup", {"stage_progress": -1}
|
||||
)
|
||||
if (
|
||||
not os.path.isfile(app.specter.torbrowser_path)
|
||||
and tor_setup_status["stage_progress"] == -1
|
||||
):
|
||||
app.specter.config["torbrowser_setup"]["stage_progress"] = 0
|
||||
app.specter.config["torbrowser_setup"]["error"] = ""
|
||||
app.specter._save()
|
||||
t = threading.Thread(
|
||||
target=setup_tor_thread,
|
||||
args=(app.specter,),
|
||||
)
|
||||
t.start()
|
||||
elif os.path.isfile(app.specter.torbrowser_path):
|
||||
return {"error": "tor is already installed"}
|
||||
elif tor_setup_status["stage_progress"] != -1:
|
||||
return {"error": "tor installation is still under progress"}
|
||||
return {"success": "Starting Tor setup!"}
|
||||
|
||||
|
||||
@app.route("/get_node_setup_status")
|
||||
@login_required
|
||||
@app.csrf.exempt
|
||||
def get_node_setup_status():
|
||||
return app.specter.config.get(
|
||||
"bitcoind_setup",
|
||||
{
|
||||
"installed": os.path.isfile(app.specter.bitcoind_path),
|
||||
"stage_progress": -1,
|
||||
"stage": "none",
|
||||
"error": "",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@app.route("/get_tor_setup_status")
|
||||
@login_required
|
||||
@app.csrf.exempt
|
||||
def get_tor_setup_status():
|
||||
return app.specter.config.get(
|
||||
"torbrowser_setup",
|
||||
{
|
||||
"installed": os.path.isfile(app.specter.torbrowser_path),
|
||||
"stage_progress": -1,
|
||||
"stage": "none",
|
||||
"error": "",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
# TODO: Move all these below to REST API
|
||||
|
||||
################ Utils ####################
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import json, os, time, random, requests, secrets
|
||||
|
||||
import json, os, time, random, requests, secrets, platform, tarfile, zipfile, sys, shutil
|
||||
import pgpy
|
||||
from pathlib import Path
|
||||
from flask import (
|
||||
Flask,
|
||||
Blueprint,
|
||||
|
|
@ -23,6 +24,8 @@ from ..helpers import (
|
|||
from ..persistence import write_devices, write_wallet
|
||||
from ..user import hash_password
|
||||
from ..util.tor import start_hidden_service, stop_hidden_services
|
||||
from ..util.sha256sum import sha256sum
|
||||
from ..util.error_handling import handle_exception
|
||||
|
||||
rand = random.randint(0, 1e32) # to force style refresh
|
||||
|
||||
|
|
@ -58,6 +61,8 @@ def bitcoin_core():
|
|||
protocol = "http"
|
||||
autodetect = rpc["autodetect"]
|
||||
datadir = rpc["datadir"]
|
||||
external_node = rpc["external_node"]
|
||||
node_view = "external" if external_node else "internal"
|
||||
err = None
|
||||
|
||||
if "protocol" in rpc:
|
||||
|
|
@ -65,14 +70,13 @@ def bitcoin_core():
|
|||
test = None
|
||||
if request.method == "POST":
|
||||
action = request.form["action"]
|
||||
if current_user.is_admin:
|
||||
autodetect = "autodetect" in request.form
|
||||
if autodetect:
|
||||
datadir = request.form["datadir"]
|
||||
user = request.form["username"]
|
||||
password = request.form["password"]
|
||||
port = request.form["port"]
|
||||
host = request.form["host"].rstrip("/")
|
||||
autodetect = "autodetect" in request.form
|
||||
if autodetect:
|
||||
datadir = request.form["datadir"]
|
||||
user = request.form["username"]
|
||||
password = request.form["password"]
|
||||
port = request.form["port"]
|
||||
host = request.form["host"].rstrip("/")
|
||||
|
||||
# protocol://host
|
||||
if "://" in host:
|
||||
|
|
@ -93,6 +97,7 @@ def bitcoin_core():
|
|||
autodetect=autodetect,
|
||||
datadir=datadir,
|
||||
)
|
||||
node_view = "external"
|
||||
|
||||
if "tests" in test:
|
||||
# If any test has failed, we notify the user that the test has not passed
|
||||
|
|
@ -102,6 +107,7 @@ def bitcoin_core():
|
|||
flash("Test passed", "info")
|
||||
elif action == "save":
|
||||
if current_user.is_admin:
|
||||
node_view = "external"
|
||||
success = app.specter.update_rpc(
|
||||
user=user,
|
||||
password=password,
|
||||
|
|
@ -114,6 +120,38 @@ def bitcoin_core():
|
|||
if not success:
|
||||
flash("Failed connecting to the node", "error")
|
||||
app.specter.check()
|
||||
# Internal Node actions
|
||||
elif action == "useinternal":
|
||||
app.specter.update_use_external_node(False)
|
||||
external_node = False
|
||||
node_view = "internal"
|
||||
elif action == "useexternal":
|
||||
app.specter.update_use_external_node(True)
|
||||
external_node = True
|
||||
node_view = "external"
|
||||
elif action == "stopbitcoind":
|
||||
node_view = "internal"
|
||||
try:
|
||||
app.specter.bitcoind.stop_bitcoind()
|
||||
app.specter.set_bitcoind_pid(False)
|
||||
time.sleep(5)
|
||||
flash("Specter stopped Bitcoin Core successfully")
|
||||
except Exception:
|
||||
try:
|
||||
flash("Stopping Bitcoin Core, this might take a few moments.")
|
||||
app.specter.rpc.stop()
|
||||
except Exception as e:
|
||||
flash(f"Failed to stop Bitcoin Core {e}", "error")
|
||||
elif action == "startbitcoind":
|
||||
node_view = "internal"
|
||||
app.specter.bitcoind.start_bitcoind(
|
||||
datadir=os.path.expanduser(app.specter.config["rpc"]["datadir"])
|
||||
)
|
||||
app.specter.set_bitcoind_pid(app.specter.bitcoind.bitcoind_proc.pid)
|
||||
time.sleep(15)
|
||||
flash("Specter has started Bitcoin Core")
|
||||
|
||||
app.specter.check()
|
||||
|
||||
return render_template(
|
||||
"settings/bitcoin_core_settings.jinja",
|
||||
|
|
@ -127,6 +165,10 @@ def bitcoin_core():
|
|||
protocol=protocol,
|
||||
specter=app.specter,
|
||||
current_version=current_version,
|
||||
bitcoind_exists=os.path.isfile(app.specter.bitcoind_path),
|
||||
is_running=app.specter.is_bitcoind_running(),
|
||||
node_view=node_view,
|
||||
external_node=external_node,
|
||||
error=err,
|
||||
rand=rand,
|
||||
)
|
||||
|
|
@ -254,6 +296,9 @@ def tor():
|
|||
if not current_user.is_admin:
|
||||
flash("Only an admin is allowed to access this page.", "error")
|
||||
return redirect("")
|
||||
app.specter.config["torbrowser_setup"]["stage"] = ""
|
||||
app.specter.config["torbrowser_setup"]["stage_progress"] = -1
|
||||
app.specter._save()
|
||||
current_version = notify_upgrade(app, flash)
|
||||
proxy_url = app.specter.proxy_url
|
||||
only_tor = app.specter.only_tor
|
||||
|
|
@ -269,13 +314,36 @@ def tor():
|
|||
app.specter.update_only_tor(only_tor, current_user)
|
||||
app.specter.update_tor_control_port(tor_control_port, current_user)
|
||||
app.specter.check()
|
||||
elif action == "starttor":
|
||||
try:
|
||||
app.specter.tor_daemon.start_tor_daemon()
|
||||
flash("Specter has started Tor")
|
||||
except Exception as e:
|
||||
flash(f"Failed to start Tor, error: {e}", "error")
|
||||
elif action == "stoptor":
|
||||
try:
|
||||
app.specter.tor_daemon.stop_tor_daemon()
|
||||
time.sleep(1)
|
||||
flash("Specter stopped Tor successfully")
|
||||
except Exception as e:
|
||||
flash(f"Failed to stop Tor, error: {e}", "error")
|
||||
elif action == "uninstalltor":
|
||||
try:
|
||||
if app.specter.is_tor_dameon_running():
|
||||
app.specter.tor_daemon.stop_tor_daemon()
|
||||
shutil.rmtree(os.path.join(app.specter.data_folder, "tor-binaries"))
|
||||
os.remove(os.path.join(app.specter.data_folder, "torrc"))
|
||||
flash(f"Tor uninstalled successfully")
|
||||
except Exception as e:
|
||||
flash(f"Failed to stop Tor, error: {e}", "error")
|
||||
elif action == "test_tor":
|
||||
try:
|
||||
requests_session = requests.Session()
|
||||
requests_session.proxies["http"] = proxy_url
|
||||
requests_session.proxies["https"] = proxy_url
|
||||
res = requests_session.get(
|
||||
"http://expyuzz4wqqyqhjn.onion", # Tor Project onion website
|
||||
# "http://expyuzz4wqqyqhjn.onion", # Tor Project onion website (seems to be down)
|
||||
"https://protonirockerxow.onion", # Proton mail onion website
|
||||
)
|
||||
tor_connectable = res.status_code == 200
|
||||
if tor_connectable:
|
||||
|
|
@ -283,7 +351,7 @@ def tor():
|
|||
else:
|
||||
flash("Failed to make test request over Tor.", "error")
|
||||
except Exception as e:
|
||||
flash("Failed to make test request over Tor. Error: %s" % e, "error")
|
||||
flash("Failed to make test request over Tor.\nError: %s" % e, "error")
|
||||
tor_connectable = False
|
||||
elif action == "toggle_hidden_service":
|
||||
if not app.config["DEBUG"]:
|
||||
|
|
@ -296,9 +364,10 @@ def tor():
|
|||
if hasattr(current_user, "is_admin") and current_user.is_admin:
|
||||
try:
|
||||
current_hidden_services = (
|
||||
app.controller.list_ephemeral_hidden_services()
|
||||
app.specter.tor_controller.list_ephemeral_hidden_services()
|
||||
)
|
||||
except Exception:
|
||||
except Exception as e:
|
||||
handle_exception(e)
|
||||
current_hidden_services = []
|
||||
if len(current_hidden_services) != 0:
|
||||
stop_hidden_services(app)
|
||||
|
|
@ -310,6 +379,7 @@ def tor():
|
|||
app.specter.toggle_tor_status()
|
||||
flash("Tor hidden service turn on successfully", "info")
|
||||
except Exception as e:
|
||||
handle_exception(e)
|
||||
flash(
|
||||
"Failed to start Tor hidden service. Make sure you have Tor running with ControlPort configured and try again. Error returned: {}".format(
|
||||
e
|
||||
|
|
@ -328,6 +398,8 @@ def tor():
|
|||
only_tor=only_tor,
|
||||
tor_control_port=tor_control_port,
|
||||
tor_service_id=app.tor_service_id,
|
||||
torbrowser_installed=os.path.isfile(app.specter.torbrowser_path),
|
||||
torbrowser_running=app.specter.is_tor_dameon_running,
|
||||
specter=app.specter,
|
||||
current_version=current_version,
|
||||
rand=rand,
|
||||
|
|
|
|||
|
|
@ -118,7 +118,9 @@ def new_wallet(wallet_type):
|
|||
action = request.form["action"]
|
||||
if action == "importwallet":
|
||||
try:
|
||||
wallet_data = json.loads(request.form["wallet_data"].replace("'", "h"))
|
||||
wallet_data = json.loads(
|
||||
request.form["wallet_data"].replace("\\'", "").replace("'", "h")
|
||||
)
|
||||
(
|
||||
wallet_name,
|
||||
recv_descriptor,
|
||||
|
|
|
|||
|
|
@ -6,11 +6,15 @@ import traceback
|
|||
import random
|
||||
import time
|
||||
import zipfile
|
||||
import platform
|
||||
import secrets
|
||||
import requests
|
||||
from io import BytesIO
|
||||
from .helpers import migrate_config, deep_update, clean_psbt, is_testnet
|
||||
from .util.checker import Checker
|
||||
from .rpc import autodetect_rpc_confs, get_default_datadir, RpcError
|
||||
from .rpc import autodetect_rpc_confs, detect_rpc_confs, get_default_datadir, RpcError
|
||||
from .bitcoind import BitcoindPlainController
|
||||
from .tor_daemon import TorDaemonController
|
||||
from urllib3.exceptions import NewConnectionError
|
||||
from requests.exceptions import ConnectionError
|
||||
from .rpc import BitcoinRPC
|
||||
|
|
@ -20,7 +24,11 @@ from .user_manager import UserManager
|
|||
from .persistence import write_json_file, read_json_file
|
||||
from .user import User
|
||||
from .util.price_providers import update_price
|
||||
from .util.tor import get_tor_daemon_suffix
|
||||
import threading
|
||||
from urllib.parse import urlparse
|
||||
from stem.control import Controller
|
||||
from .specter_error import SpecterError
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
|
@ -113,6 +121,7 @@ class Specter:
|
|||
"port": "",
|
||||
"host": "localhost", # localhost
|
||||
"protocol": "http", # https for the future
|
||||
"external_node": True,
|
||||
},
|
||||
"auth": {
|
||||
"method": "none",
|
||||
|
|
@ -135,14 +144,63 @@ class Specter:
|
|||
"alt_symbol": "BTC",
|
||||
"price_provider": "",
|
||||
"validate_merkle_proofs": False,
|
||||
"bitcoind": False,
|
||||
"bitcoind_internal_version": "",
|
||||
"bitcoind_setup": {
|
||||
"stage_progress": -1,
|
||||
},
|
||||
"torbrowser_setup": {
|
||||
"stage_progress": -1,
|
||||
},
|
||||
}
|
||||
|
||||
self.torbrowser_path = os.path.join(
|
||||
self.data_folder, f"tor-binaries/tor{get_tor_daemon_suffix()}"
|
||||
)
|
||||
|
||||
self.bitcoind_path = os.path.join(
|
||||
self.data_folder, "bitcoin-binaries/bin/bitcoind"
|
||||
)
|
||||
|
||||
if platform.system() == "Windows":
|
||||
self.bitcoind_path += ".exe"
|
||||
|
||||
self._bitcoind = None
|
||||
self._tor_daemon = None
|
||||
# health check: loads config, tests rpc
|
||||
# also loads and checks wallets for all users
|
||||
try:
|
||||
self.check(check_all=True)
|
||||
rpc_conf = next(
|
||||
(
|
||||
conf
|
||||
for conf in detect_rpc_confs(
|
||||
datadir=os.path.expanduser(self.config["rpc"]["datadir"])
|
||||
)
|
||||
if conf["port"] == 8332
|
||||
),
|
||||
None,
|
||||
)
|
||||
if not rpc_conf:
|
||||
if not self.config["rpc"]["user"]:
|
||||
self.config["rpc"]["user"] = "bitcoin"
|
||||
self._save()
|
||||
if not self.config["rpc"]["password"]:
|
||||
self.config["rpc"]["password"] = secrets.token_urlsafe(16)
|
||||
self._save()
|
||||
|
||||
if os.path.isfile(self.torbrowser_path):
|
||||
self.tor_daemon.start_tor_daemon()
|
||||
|
||||
if not self.config["rpc"].get("external_node", True):
|
||||
self.bitcoind.start_bitcoind(
|
||||
datadir=os.path.expanduser(self.config["rpc"]["datadir"])
|
||||
)
|
||||
self.set_bitcoind_pid(self.bitcoind.bitcoind_proc.pid)
|
||||
time.sleep(15)
|
||||
except Exception as e:
|
||||
logger.error(e)
|
||||
self.update_tor_controller()
|
||||
self.checker = Checker(lambda: self.check(check_all=True), desc="health")
|
||||
self.checker.start()
|
||||
self.price_checker = Checker(
|
||||
|
|
@ -443,6 +501,17 @@ class Specter:
|
|||
self.check(check_all=True)
|
||||
return self.rpc is not None
|
||||
|
||||
def set_bitcoind_pid(self, pid):
|
||||
""" set the control pid of the bitcoind daemon """
|
||||
if self.config.get("bitcoind", False) != pid:
|
||||
self.config["bitcoind"] = pid
|
||||
self._save()
|
||||
|
||||
def update_use_external_node(self, use_external_node):
|
||||
""" set whatever specter should connect to internal or external node """
|
||||
self.config["rpc"]["external_node"] = use_external_node
|
||||
self._save()
|
||||
|
||||
def update_auth(self, method, rate_limit, registration_link_timeout):
|
||||
""" simply persisting the current auth-choice """
|
||||
auth = self.config["auth"]
|
||||
|
|
@ -493,6 +562,71 @@ class Specter:
|
|||
if self.config["tor_control_port"] != tor_control_port:
|
||||
self.config["tor_control_port"] = tor_control_port
|
||||
self._save()
|
||||
self.update_tor_controller()
|
||||
|
||||
def update_tor_controller(self):
|
||||
try:
|
||||
tor_control_address = urlparse(self.proxy_url).netloc.split(":")[0]
|
||||
if tor_control_address == "localhost":
|
||||
tor_control_address = "127.0.0.1"
|
||||
self._tor_controller = Controller.from_port(
|
||||
address=tor_control_address,
|
||||
port=int(self.tor_control_port) if self.tor_control_port else "default",
|
||||
)
|
||||
self._tor_controller.authenticate(
|
||||
password=self.config.get("torrc_password", "")
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to connect to Tor control port. Error: {e}")
|
||||
self._tor_controller = None
|
||||
|
||||
@property
|
||||
def tor_daemon(self):
|
||||
if os.path.isfile(self.torbrowser_path) and os.path.join(
|
||||
self.data_folder, "torrc"
|
||||
):
|
||||
if not self._tor_daemon:
|
||||
self._tor_daemon = TorDaemonController(
|
||||
tor_daemon_path=self.torbrowser_path,
|
||||
tor_config_path=os.path.join(self.data_folder, "torrc"),
|
||||
)
|
||||
return self._tor_daemon
|
||||
raise SpecterError(
|
||||
"Tor daemon files missing. Make sure Tor is installed within Specter"
|
||||
)
|
||||
|
||||
@property
|
||||
def bitcoind(self):
|
||||
if os.path.isfile(self.bitcoind_path):
|
||||
if not self._bitcoind:
|
||||
self._bitcoind = BitcoindPlainController(
|
||||
bitcoind_path=self.bitcoind_path,
|
||||
rpcport=8332,
|
||||
network="mainnet",
|
||||
rpcuser=self.config["rpc"]["user"],
|
||||
rpcpassword=self.config["rpc"]["password"],
|
||||
)
|
||||
return self._bitcoind
|
||||
raise SpecterError(
|
||||
"Bitcoin Core files missing. Make sure Bitcoin Core is installed within Specter"
|
||||
)
|
||||
|
||||
def is_tor_dameon_running(self):
|
||||
return self._tor_daemon and self._tor_daemon.is_running()
|
||||
|
||||
def is_bitcoind_running(self):
|
||||
return self._bitcoind and self._bitcoind.check_existing()
|
||||
|
||||
@property
|
||||
def tor_controller(self):
|
||||
if self._tor_controller:
|
||||
return self._tor_controller
|
||||
self.update_tor_controller()
|
||||
if self._tor_controller:
|
||||
return self._tor_controller
|
||||
raise SpecterError(
|
||||
"Failed to connect to the Tor daemon. Make sure ControlPort is properly configured."
|
||||
)
|
||||
|
||||
def update_hwi_bridge_url(self, url, user):
|
||||
""" update the hwi bridge url to use """
|
||||
|
|
|
|||
784
src/cryptoadvance/specter/static/bitcoin-release-pubkey.asc
Normal file
784
src/cryptoadvance/specter/static/bitcoin-release-pubkey.asc
Normal file
|
|
@ -0,0 +1,784 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
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
|
||||
S9Pb9RpTJvsxc4FTeX7vTHtCosUVKvvK
|
||||
=D7TT
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
BIN
src/cryptoadvance/specter/static/img/party.png
Normal file
BIN
src/cryptoadvance/specter/static/img/party.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 34 KiB |
|
|
@ -0,0 +1,12 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
mE8EX3YN+BMFK4EEAAoCAwRhTEFptcVn6WAE4PbCTCeeHRmqQ5Yd49wCdycs9Sje
|
||||
2B/rBPEGADac9PRmIXhjtWn3pifZ/sG2hFzl7G3VGIDttElTdGVwYW4gU25pZ2ly
|
||||
ZXYgKFNwZWN0ZXIgcmVsZWFzZSBzaWduaW5nIGtleSkgPHNuaWdpcmV2LnN0ZXBh
|
||||
bkBnbWFpbC5jb20+iJYEExMIAD4WIQRvFuNU+DOT1uUuwl827TV6skuRXwUCX3YN
|
||||
+AIbAwUJAeEzgAULCQgHAgYVCgkICwIEFgIDAQIeAQIXgAAKCRA27TV6skuRX9P2
|
||||
AP9LUiI/IapEK0YcRFczaBGyRC2/p8AvrKKrK/8hY6evfgEAo00jrdk10duMaY7N
|
||||
kpfNcQIQy6Qhv3HDg8AcY1RgUyk=
|
||||
=9s5S
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
332
src/cryptoadvance/specter/static/torbrowser-release-pubkey.asc
Normal file
332
src/cryptoadvance/specter/static/torbrowser-release-pubkey.asc
Normal file
|
|
@ -0,0 +1,332 @@
|
|||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
|
||||
xsFNBFSOr7oBEADQMs+Q5cAshRhj3YkKgCBKyrjFWMZqVhlf9Y3ePtFQ9kFEnYIS
|
||||
G9rzMhFC6KMXPn9bgg6OBPPUnnJ29UsKvAk+qa8F35R+s0ZXmPRfmv5/6PqxLOn4
|
||||
G733K67K0/eXYW1mTkz9sjY8u9E3T10JNT0zE/60WihuZGKZQDIqqig0fOsdvdGa
|
||||
g+srAW91T56kAT+y59VcvqVCQNjS897E3T9hsUNkQNCdOitQcnN8/5VNQUL0SjyD
|
||||
BV0y5ry+pUt1rnojj82KQ3WzZuD+XsDE+w2JSGqhcqf9b7D6puy1smhCNwZJ9L1l
|
||||
pJlrCap6YQN8TPFTkf4aFBctxonAdQDDxbON6sPJALc/myPwTVTxD3nJJhv12yft
|
||||
2iwZLaCJcdq6tp96re1dwaETpvvKeWqhWGVkmNaAPhShcCKpVYC3+Jil6nTqN6LI
|
||||
hKD0ILBGOT/2/Rxd4kj1uDzvc2RVHe6LKLc5EQYO80/wSIL8LMdqZSX2R/AnhcNg
|
||||
G/k7yOQWWNY7RPU1cV+E9QKNwqS4Zj2VyU6s6ikaPuUnjW59iMkSGUuS+gJUR2hp
|
||||
jOKjNzu8vxbotBgZ01upDUdl69OnR1dv9X+bMzGWUyOjAjK6SP8rFtWFBjWgWcED
|
||||
OHu51YpicSdN3uf7lppEXGx91n45xVMhL9d2KNp3DhWkKDuWhdliWC/r1wARAQAB
|
||||
zUBUb3IgQnJvd3NlciBEZXZlbG9wZXJzIChzaWduaW5nIGtleSkgPHRvcmJyb3dz
|
||||
ZXJAdG9ycHJvamVjdC5vcmc+wsGUBBMBCgA+AhsBBQsJCAcDBRUKCQgLBRYCAwEA
|
||||
Ah4BAheAFiEE724obdqF6ipLp95oTixuh5MpgpAFAl8XqaAFCRPu+2YACgkQTixu
|
||||
h5MpgpASEQ//fiGjtuwF+xAB5366e0ciTXKTKq2ar2uBgeKnAl7h862ePLE8MwIN
|
||||
2d7t1eGBdyr1B+CK6XRkeHtRjN5feOLOKQYy6UkPfSZZnSt/pXqH9bCZWIlejpFl
|
||||
HaNAUGFMbmtHzJb4ZEto3B0/HGAAx/1xiHP5GspdEj99H2T710axz5mCqbt6BRv4
|
||||
twZCEWQ4LE1GGn1NoBaf0STmF7luKC3IQi/H2VSc2LTJLQoo5Lnmr/w+jZ4N9S/J
|
||||
QKfeYQmXplbHWtG+AQh9VxDJxfK8z85zwvosR0LuUpbvn9Jsn8sFwB2TA9jLzPNr
|
||||
trBeotx5kcQm1ae+ETiNQdtJ8JzFHm5a5UmViZy6/zyK0T4PisKu7J10mZ9bBBro
|
||||
RXuqmxWqnD4GV/knKECE7K2DUeS7HsJin/hVc2OaHckII1i2Ced64tVfP9I1H/QX
|
||||
HXeP4AVkeDnwPTVDB/1R3RCBguqm0fkqGBW9HNTQz8ju6hiNdtTtLBFQ8rYaMO8U
|
||||
YVfQBFtuh7zKwjSnt0gsN3J/FEcHMIDto5mkerL3GrEnBZeXV8M14BdBOKiw2swK
|
||||
ibVuXhmW8nWdKO7evK8O+xE7W6wE+fWCghW3VLM8tnVlpMkmTTxQATbZ74Fhfor2
|
||||
DT8Obn8D+IK7Vzv2NJbtX9j1S8bz9t0JCuKIHRClF7ijJ0NyQEM6xbLCwZQEEwEK
|
||||
ACcCGwEFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AFAlXdokUFCQq09AMAIQkQTixu
|
||||
h5MpgpAWIQTvbiht2oXqKkun3mhOLG6HkymCkAETEAC0OHILapQC/bVKHzatpPzK
|
||||
lmlF/7ABoqWSj6Ryr0gVIG5ocD7B66lw/AcStv6c73ApKOPHB4EetJbpjWZdmIe9
|
||||
o9z/Gs1X5jcQMH41y/EIPutg0YO8PoxKRTJpLqFe4SDd64QsXTpwYOrT+1d1b8cE
|
||||
WKxIO1OaviQMx/GtJqC9/E5TKyXJANDrIeiknaeZU4sJ0DGL9Y5jABsyJKOo+SNd
|
||||
BUMRKKT5U2fjFq9mGXjk2dNtxo7nQOn/mU50TA1fyDBVcVnQ1bIRYga8yOWQcjEo
|
||||
yfnXiAbRW2N+pr0MRWBvbv32fEZGI5y6Wodt09qgf5lnTh87fO71KnAN6mozeh5H
|
||||
QwgzYuU44jqunj3J1vRlj/ZVsAwfmnDuVa6RUHeH7iyLVhRgpeEeD8wLhym2sxLx
|
||||
NUUTlbh03dfgabRGEJFuDR5nWU4ckuG0sATZlPCGIu+iT99NhOCSQXlsGxwOopVk
|
||||
xDImhiKeLiZmF4XQ5xM1KoO9nniRz8PWloG940GdwBJEBZCZRaeJRZ06R3BVlJPM
|
||||
nlJYzNLldaC7r2sf/FefAzjEjffg1QXZ2+iaS5biHdqB/QWO7zHWP39hHQMrCT3z
|
||||
/zfT4NLFlCe8zX4hwEFuoZ1mbggaSblYL5FHlvx77/TFJjYLRr4nrHDLWVFNihUR
|
||||
nlOADxJPAo6iTeU/qkssysLBjgQTAQoAIQUCVI6vugIbAQULCQgHAwUVCgkICwUW
|
||||
AgMBAAIeAQIXgAAhCRBOLG6HkymCkBYhBO9uKG3aheoqS6feaE4sboeTKYKQsBUP
|
||||
/jPbrHHGo1G3ndP+Mayofvd1PNP9q995SzNFqHgE3TWHUt3Q4M0fSMV/iyOZ3QWC
|
||||
lzxvs64LcWn9DYRJP79ptHOLppCYA4xn8SwMdpmMGEQqyiOdorIyvsAmV8N6BJXE
|
||||
ozizRAm9WbKmdB8qkpcb3ZcOH9L7cr9mXbaMzFnfyrPpPYAHJNxoXjy8xJV/TFfQ
|
||||
aHG7mNRGPBFfR7PNv1LAkVR148Sx21K9m4yikojHTPpJ5R0Qe/zLj4Igc4lqpVNj
|
||||
aL05s8uDy4e4kXzOMnlZiF7VQeIwxJsaxiEagwQRemAXQT2NEwCOwwT11kLdyBT0
|
||||
MtmLmoS1gQiH7FELl1Xb8vuW5I/HBo/3SC6hOUIVLfgSNtqcnE4J9DPfzs91UlDS
|
||||
WlQgd+67CRN+OQLuJkaA46h8kN9ZzjVxw23EISB8Oq4PiEtOztk+gGSKHEmUfuTA
|
||||
Q+gHY0KimWWKhaAB7wy+w07QTbAkd7e8fkzOeyYplJ+fxOhbwE1qgbU/lwmSX62M
|
||||
6qKRDa9a7VLfGyty5baCMijBdEiLxd+S5YTa+wP0Iia9rNKr+nA05rj0P/Lw9tSY
|
||||
95lNf9siHqHHWPpWezkUax4C04GovykJfrYMH2EAOg87wOQeUTYBGp07JddCd2Hh
|
||||
UsutN/X6CMUxGYPaFcAdZr5Hz0TZVVM8oWYPN/6uBsepzsFNBFe9hGABEACrWZ8U
|
||||
wcxWOOB9vscJl0FP6r3bITq3zItycPFGzqaMmQLP+kYJ5eVLp8rskul0IkmgcxWq
|
||||
Geek7hBZ1b7vZjD8Cq1UpUbLQHh1zPdZ3eT91uojO2feJmeIkTc/fHuaQfOsO4Lf
|
||||
kU/YjSyIbXWfe8VURcNUIt+nBaPNHaFxCb9T3kvuURu0uYoxyu5fYM1ln9MnrFPL
|
||||
jYWHnteBT448go3//vPrvqNk4jNUlWIhf4/SBjF+xWlqOJ+NzR0+DmCcfAYR7MPj
|
||||
QzH5SvilGBiylKQS7N8ljZZzWAk9Eg83NHXQ8EGHS7AGwpZM1XsD3zVdOmyIoO4b
|
||||
kpuyR5SGGLOHhil3DETmcUjNkXMGxf/kwu9p+bLOrdaP6q/ui+Cj1yycjJyAVs5y
|
||||
w4kPv3Hzi4eNRhdm31/hbaKi2mSrBcj+32eKOEQ31tvKwRAq4HkOnH0sJ+fzg7lM
|
||||
897Hruce+Y8l+VbwzCQ43UjUBMtvCDbTtl4YxRcseMGAmOf0ReW1p7+jkYVQXqAH
|
||||
QAUy9E6XXuCzC/jjtGuqj9in71fGv3OxTzPZkWrxvAhoDorQZiMmlKYxg9PSoGKp
|
||||
//e+8aTW9/mEuGoJyFhjaeHzMc0IlgZHql2xhBctEvYJl8ddrcZl3k7ssXmMVJvb
|
||||
AqCJuUZ8aBaIcYauZbTcAdfwHMv0Z+jDSmsR0wARAQABwsObBBgBCgAPBQJXvYRg
|
||||
AhsCBQkDwmcAAkAJEE4sboeTKYKQwV0gBBkBCgAGBQJXvYRgAAoJENFIP6bDwHE2
|
||||
+rkP/1HMxGVRc7DUsSAc3w+2oB3lEXvmfYkx9UJ6QD9eHY0Va2I17U85liD3S5kA
|
||||
kTm+tHzXj7UKu5odQe4QkvFuZ2ON2WNdQUfftkvi48+BvpfKeHQpbFu34DgSmRAi
|
||||
6DVR1HzoXFt+tu0H4qk1i1v23a4UGfgowABklvM+t7fcuvyENqbyMaEz34t+zauS
|
||||
9bVK41M1hPB6HOmx1zduObRTqUtaaQ2AqptAvV2dt0I7J90QJ77R0+KT5wTesGn9
|
||||
45W3R+CG+Ks0QyOJLyEHgoNmmdgiJcatYoUf6+ZU9FHn/kfbDqVX2o+b9LgkUGdq
|
||||
+mNWnwnwsul5XQeCwMMse8mrRPeYT4MaLHFFBtxeQuVBXM9cErHg/HUdCvij+4AL
|
||||
mOiqhlGOYkDfGXy8YhSvPZZd46vaosX4ewd236frJdGngdyKvvVjXmzdOlFX54ND
|
||||
XT6JW5I+SVbmJ/cTSz4NmXUfYbTnfiLHgM/J5v8gnSmQ4PeFALBoub7+UpXd3J40
|
||||
Fnv23WpKZbRB+II3UaFF8udIK4x/iScBYy42MEAyEjyv8HwYBgXzqMN1XIm7DJRf
|
||||
qcIti7HvGqX4Wg/hcqxvxRBbD7n9JGpGD7AON4KqrkxIFgsIH/Ai54qx3sJ0JdwP
|
||||
G9rz3qzlrPFkOA65whrbt1N1d84ksBIB4NRaTpeQPZ4dRBdvFiEE724obdqF6ipL
|
||||
p95oTixuh5MpgpD8HRAAxuHnnIjqCvTfzKp/rAIniT0uDpBccAY8qoQm9fGHQzM6
|
||||
GNbi6fBnUBoQlSiRrRAJlpeDFpSedAHbUyGIGM+kYPF8eD7PKC3j2YnU8nqzUslf
|
||||
CNqOIxq0ptNi0173z5NGurhlkoQbzEH34GGlXOvd/XVfnOed+pcDLWiNe/jlzw2S
|
||||
zB21oBBu9hCmRm+A3YpxWdWQWgYCKlItnEW8AR7IOTjy1R1Q9fhOvm7yoDr/Hp3n
|
||||
RFMti/ZqEQ9ZKfrX/W/5QPn/+1ThWd9jDTH2YTsjW/lM1dPBVBj17Bpp1hTaGU1h
|
||||
WW6ILCapFzKdfcBCG6OpY2H2hHrksIJ1JAlawR669LtBSXzQhGU/IPXeUAOLJvhK
|
||||
nnrKECQx8y3sAeUh/Z36qmH0HZ20fVZndK5XI5lKomZrlXnFNc3fF3KHrx8hlPrr
|
||||
ISEG9qRJW24WOnHLo1GVwPpowroE9l/YJO583qLRvNwwvntGJdzVIm9XbiGnETy9
|
||||
P3HWLfvQpvEeJAsY5k2RTaTe2pKeiA367uuYAMGctg34LngbgTYjecKfQbVuF0DN
|
||||
Yv4hktuP63ODh1a7JfQctFsB1EYiCtyx0VIGiLwwz/VBzS/wgkBiTQKMaCJXEmwH
|
||||
G2iZCldyCKhl7Q+MK4y8kSvlsQAUMDKnCvadGzp2ShmWg67yVNr1lNtCngNbrdjO
|
||||
wU0EWwnQdAEQAK8MRUxjsvvZFGt3dScnI20cvlL4LM3ReesedqrFEOcZP8q5kVxi
|
||||
u3n1zT64BXRza2EiYPttymbh56ynLk/SxxcM1qdGhn1qwdCEav//TYJK4eE0ZRPb
|
||||
dOL6YY9hkfBPZUONBD+YYnohsOUnAYRNaRsSjlAwsaoDauJMSOGl7Fam0E2GDvzA
|
||||
YeCEjE9SYFwW1jGGmvEKjAV9zqjeMwH0A7NWYuTo9VXmCyxRPPhAKw/3XsMuJ1WO
|
||||
nL9rPz4yo2ZQlyLf342IFKpvQLw+H3NqRYpJa8P5bi1cKYGHP97UZFHLOu5rzvoy
|
||||
FmTU82H0oc2BQDIacZzwmVVwyM0wOg4evdRXmv/2KvtuwxosF58kXZTtCQ7miWRW
|
||||
UPGMX4PR71I70KBBDcLWZMptmW8Ey+oE7DjOUMv77sGeBZdF/pBW0Oy4qgNF/tX/
|
||||
/NkoI2dKBBuEQMikQLgfuEoGF/5zueWJdLjEDESeZB9nXgWEaADwiH+nzcuUYivC
|
||||
YR2yszRpRTv2GwcUoYbT1Cb8L3Qy77xq21BiOxs5OWylfUS0yLZN9XOP/qwa5MDP
|
||||
mpS1kAw3IcBf9MA825PFxXQY/mv9rvd5gmip+vfwBT8F3ZvXzQHAWBF0bXTONmp/
|
||||
CxujjE1PkJBQ+mOg5x/wyYEVw+HkfZgSIwfVFFJ+MXkKYeXXVHg5+lubABEBAAHC
|
||||
w7IEGAEKACYCGwIWIQTvbiht2oXqKkun3mhOLG6HkymCkAUCX9bPawUJBbpM9wJA
|
||||
CRBOLG6HkymCkMF0IAQZAQoAHRYhBBEHdbXRAfs2vGyRG+t3RJHZ/wbiBQJbCdB0
|
||||
AAoJEOt3RJHZ/wbi170P/0Rvg0uBdrHsnKOjfqwPZ6k4I1Wwx2tpkrsb4wKVVxoR
|
||||
TZFOiF1DE33gZ1Dkf1jczfwdpBZQbC70gkaLz2WpbbBRtg6RIZQO53psM+wmYRO1
|
||||
C4P+ByMJ9bf8V28pzKTsBV3jU3ACp2uNWft7wIzUq/6AHHf6RgVvikglC+RcUxLb
|
||||
OHdXzLpgM8ItzotD3UBq+g67um3VG1HC3/1RaA+tqvrg/uehEjhiC753XYgz2VkH
|
||||
lRVVoM74KoXC3HKxChwArpT5lAfqwUwvcvvOiPL4ZsBivl452tQOPfef9XBgV7LS
|
||||
QnS1Wy8x0L+ncClP8OiEdC3DpZSgZKeFJ1V+xsis9V0Bzs/Pv4VH+s7spvm1Al7B
|
||||
wkNXQkxn2csaws1lIuS7cfB6sbdkNNGxAeQSOfCLOCWJU01niy/NVRQiEEjbhCSM
|
||||
Hwj1AEf6B+sGghmj5BEfCdfB27o4eCrv/xQCJri7g0FlYFypnaxc/lHojNQZZCAH
|
||||
A3Av/RCcbEoBQ1JBdR2f9oUQ86ZJtHVbUKU4k39jSA/b8eqSQQB6y/2dkNdW2VnX
|
||||
iM/Ai8aj1FJQTX4K+j5pCGy/+AR241qfeImr3JtMav2SrkfuD72dUPbKowFeKq3M
|
||||
0p3912peoH82PVnLgsD7uhRTYbhLUOefYG8JvoovnhQH6X9RvMEQ3aZyvRfgkNyR
|
||||
yBsP+gLZ3+aX7f/9zE4NigvtqZrPSL1esr+h1BHZVVTFGfhjAUjuhvR+v0DtGL+a
|
||||
yLVfIVAM/MOELej46htLFNC+1pwVevPOtxxvfpdNIS1lyvCiD0vNIMPEMt4sZoe6
|
||||
GaTA1tRMBVyY+C5PjmBUOrFPDfFAIEct1OoCDqeDsyDFhR6hNbOz1hXj+r5z4Dja
|
||||
AqF8IyrGdAnC7iLCjdjgkiQRLvqFbXOR3nYxDkZnLUKZYmIspz6jAvbXJC/H8Ob0
|
||||
sFMsOK4+mv/lgheU2aoc6fKytxxX3l37MSxuBP8Q8HqPJ6uR2Fkpljcu4O6gl331
|
||||
Kgf+LnF2gzw/gCc+sVIaViK8RgjkAasHriQ9EWoVdE0PD2xzEzer0xG5kayEOnG2
|
||||
rkPbxE8iDZ1oS/n3P3H5aylXGiQ2iu7gPab1uz7I9pCynMU6LvHbav7pptZPnzJg
|
||||
H9bPH3BbhMomsfUwtmBirTA4+u3SFOxOEg99Fx38eyG/KLUvsGJnc9BliFCl6oFO
|
||||
LRdYYfZKtd616RvjKlJiuZ8ELe7Gs7e8UU3x7pRpC+ODtb7JbtsXc2Wp7v6miaAu
|
||||
0h54+FsfG3MJb0HgjLhCU4canCUgaPD4kvaYuZiDdFcW9oH9s4FGXazlJ2GxlvKV
|
||||
KR8ylK+uWmCh7QT33xX5HqlndvL1gKtKT2HkV0WyVRWcCDhXwsOyBBgBCgAmAhsC
|
||||
FiEE724obdqF6ipLp95oTixuh5MpgpAFAl8XqfEFCQTTmn0CQAkQTixuh5MpgpDB
|
||||
dCAEGQEKAB0WIQQRB3W10QH7NrxskRvrd0SR2f8G4gUCWwnQdAAKCRDrd0SR2f8G
|
||||
4te9D/9Eb4NLgXax7Jyjo36sD2epOCNVsMdraZK7G+MClVcaEU2RTohdQxN94GdQ
|
||||
5H9Y3M38HaQWUGwu9IJGi89lqW2wUbYOkSGUDud6bDPsJmETtQuD/gcjCfW3/Fdv
|
||||
Kcyk7AVd41NwAqdrjVn7e8CM1Kv+gBx3+kYFb4pIJQvkXFMS2zh3V8y6YDPCLc6L
|
||||
Q91AavoOu7pt1RtRwt/9UWgPrar64P7noRI4Ygu+d12IM9lZB5UVVaDO+CqFwtxy
|
||||
sQocAK6U+ZQH6sFML3L7zojy+GbAYr5eOdrUDj33n/VwYFey0kJ0tVsvMdC/p3Ap
|
||||
T/DohHQtw6WUoGSnhSdVfsbIrPVdAc7Pz7+FR/rO7Kb5tQJewcJDV0JMZ9nLGsLN
|
||||
ZSLku3HwerG3ZDTRsQHkEjnwizgliVNNZ4svzVUUIhBI24QkjB8I9QBH+gfrBoIZ
|
||||
o+QRHwnXwdu6OHgq7/8UAia4u4NBZWBcqZ2sXP5R6IzUGWQgBwNwL/0QnGxKAUNS
|
||||
QXUdn/aFEPOmSbR1W1ClOJN/Y0gP2/HqkkEAesv9nZDXVtlZ14jPwIvGo9RSUE1+
|
||||
Cvo+aQhsv/gEduNan3iJq9ybTGr9kq5H7g+9nVD2yqMBXiqtzNKd/ddqXqB/Nj1Z
|
||||
y4LA+7oUU2G4S1Dnn2BvCb6KL54UB+l/UbzBEN2mcr0X4JDckVwWD/kBD9PVxWFT
|
||||
I3NN1cgQy3AQT6lB3D2KF/yf7X6vX9CMpcBNcDzQPD6Gx4vfXiqkImocZHmYFLo2
|
||||
g0wzhqbBSiwhRcl7xD8Ab+Yrrw6zUVy4cCFzRXab8+oI6OpCNkp/HEx319cGxsjf
|
||||
Rr7aCPemoJi7b+o4JiwvY+x3/oDzP1Ycm2y9jAhTxu9AdJMZxMhBcY+uHuZBymS0
|
||||
cwjpfn4sE7GhF/S/c++DrcfLlzs4dWRyrQqT5ODf136vfV4M5l1b1Gh+hQ0omnaO
|
||||
hjfltPMwnMz05YQRxaVUUF0m3scP5koKvRPhaYoaizpq0HQMtm+kM9+l/BFR8tjW
|
||||
zSW64zPcMy8XMX43/GnhfckuvwgwHR4j0UCjS6/m7RQgE912fU2AQsE+2D3bitaV
|
||||
7C74+MW0vmzmeu2F0zRVFyyzzXoXJ8CA+L26YOsm5tVU+votzM+3puo2BILWXtHD
|
||||
vEvQI8Sx57EyG2pPJXxlLY7gJd49JqUY3N+LQyzYguzpjE0uJSVA8p3pQS/FF+ah
|
||||
5kf9Ku9D5Y6BknFkT899tNIv3+OonVCJxD8NhfwNYzx/hIDp1Ql4xYpuLFyNNod7
|
||||
2D3HNSqBSKQGxBPbyf+0TqAvFeuZiJAQCsf/OC1/PY8vZlgSMe6x1FEtbc3TC5v8
|
||||
LVU/EwJt8XIiTsaupTnX6LMYU55tb+UlRMLDsgQYAQoAJhYhBO9uKG3aheoqS6fe
|
||||
aE4sboeTKYKQBQJbCdB0AhsCBQkEU2wAAkAJEE4sboeTKYKQwXQgBBkBCgAdFiEE
|
||||
EQd1tdEB+za8bJEb63dEkdn/BuIFAlsJ0HQACgkQ63dEkdn/BuLXvQ//RG+DS4F2
|
||||
seyco6N+rA9nqTgjVbDHa2mSuxvjApVXGhFNkU6IXUMTfeBnUOR/WNzN/B2kFlBs
|
||||
LvSCRovPZaltsFG2DpEhlA7nemwz7CZhE7ULg/4HIwn1t/xXbynMpOwFXeNTcAKn
|
||||
a41Z+3vAjNSr/oAcd/pGBW+KSCUL5FxTEts4d1fMumAzwi3Oi0PdQGr6Dru6bdUb
|
||||
UcLf/VFoD62q+uD+56ESOGILvnddiDPZWQeVFVWgzvgqhcLccrEKHACulPmUB+rB
|
||||
TC9y+86I8vhmwGK+Xjna1A4995/1cGBXstJCdLVbLzHQv6dwKU/w6IR0LcOllKBk
|
||||
p4UnVX7GyKz1XQHOz8+/hUf6zuym+bUCXsHCQ1dCTGfZyxrCzWUi5Ltx8Hqxt2Q0
|
||||
0bEB5BI58Is4JYlTTWeLL81VFCIQSNuEJIwfCPUAR/oH6waCGaPkER8J18Hbujh4
|
||||
Ku//FAImuLuDQWVgXKmdrFz+UeiM1BlkIAcDcC/9EJxsSgFDUkF1HZ/2hRDzpkm0
|
||||
dVtQpTiTf2NID9vx6pJBAHrL/Z2Q11bZWdeIz8CLxqPUUlBNfgr6PmkIbL/4BHbj
|
||||
Wp94iavcm0xq/ZKuR+4PvZ1Q9sqjAV4qrczSnf3Xal6gfzY9WcuCwPu6FFNhuEtQ
|
||||
559gbwm+ii+eFAfpf1G8wRDdpnK9F+CQ3JGB8w//R1oVLXREFhyoS3xWRuRgVCsX
|
||||
DCXe0z2bX6vRmxX6R3ebqNTGP1kMfS4/HtiE7tTSe+1ItYGuK/SU6oyRSpl01ZIK
|
||||
Rkw6XpMlbSFOQYIWvzJk/fJxOCE/+E8UmP8BE0Mm1Ch2VZZd9nBFINkijYPDsghE
|
||||
NgG8vGnXsWcyQUgsegrYJpk3kH6cTxn3VHWesn3Ztc3BYgdvd3S37TVNcQF06TuQ
|
||||
c1tpsHDD9sh2DoPiqP8svgX4faJH/oEykDSBZ2lwgIQSZCdCR/SB8cqfeUBmypfU
|
||||
Wz69TA9IJd2VZSRQ+4gyil6dTw9MzR/mMBORthRVx8vjm3KqeE+hXcPNP4D7Uq1H
|
||||
e8aRXEcZOsJ66wWYDOQNJZO5aG/wtA3MCu1Jqbf1wqKtl8g8ccmu8vzTd9njTwHi
|
||||
27no1JlpP8eowX7BWB6B7gCYZadGVo625fAsgqam3ygqhTMKDomRcABjMCu3W6eh
|
||||
FqZsHU825sXIRkaUN4Ba1ZUPgZEWcBPpSlOIEl6GdTFAQ1PUrYtaeIRehRs0u2Wj
|
||||
4fIEdSM9KMLiAiMYtDrf5x3avyGSBPeHjOR6fL3Sk0KAmcWKefpRNxKiZOFRaJcm
|
||||
ao54rUOZOo1Z+J6Y/AV0pdICivkEXs9LezFELKx/0lLzPdvxcY3vFEatq1o/YRYM
|
||||
P2powW8wGg4CKSnufbnOwU0EVI6wVgEQAMHv1i+IMWNjqbCMUPS7DXTm4JGw1F88
|
||||
gf3YHVwSF7dZfUdHS4fb65fVs4APafKZqQral0qjDIsI2oZgued24/IDF9JIYWX+
|
||||
Kks5WbHJP3k7Gk68wDcGBnGMSoaKvOQtc/L3/7AHnhxnFvtsD8aydtUfBNIBBiNz
|
||||
AXsfpP/Wf+tGV6z+7U+CT7wE3UCot2Erb/Ud5PVSq8GMHZR+Io45XbDZEHCtmWgs
|
||||
3l+z8zh4LnFf3HP+UuDNl2PCmxAql+WXOI3NbB640O+4Y4sIxt08C3wyzKuGH6kI
|
||||
lRvXGvY7kC+JNfn8oMG1knKeO7Wf3dDzuUK9QvCGxNm2zyObvzzt9+lCvjJndpNE
|
||||
UEDle8bo3xajQBexm6CFYI3luZjEbLKMI2FY0Wl3R2bEmCk3G+nlQlmc/+5s7qmY
|
||||
ZSo/ZnvgoUDTUMhvVypgTkydK4+zoQuyd6qFHbeKWaPr6eZIjdO5J3t5v+2WbFjA
|
||||
qXW/89rHv9aVaZiWL7DksmrBgvxWIHriTdwptDfNYeFOnICQkCHqUoWQ2WgrWDuu
|
||||
oXs+t0bOna6NJHKSNZn2G4mHZqi8tUv22ImkYaIVVDmkoGcidbPv16nDvnoU6m22
|
||||
fi9CKoqoFVNKwyLRz7t9VqCM2Z2Tqqc5PGvw3of1chqIOV18AD/fk1kHtfKcn9L9
|
||||
A9X+bXuFUg+dABEBAAHCw5sEGAEKAA8CGwIFAlXdomsFCQURWRACQAkQTixuh5Mp
|
||||
gpDBXSAEGQEKAAYFAlSOsFYACgkQLhrGjtQIFOCSoxAAl61KCTOIrJBf7UQnOu25
|
||||
f0FlcuEQ0JVYXAn51AJ7iK++n9xI7mKqw+nPEGnG0xc4IE8LbWF8dPTH9wN/PE/4
|
||||
2R1f/R5Dxt3MaYKTZqv2IkIcQlb3bx6PyBpKUvVDLeH+4RY7C+3ZVtee8rPDkPHI
|
||||
EUkNAPxRHjL8xOrp4u3GGeJXy5K0vPeqNJWsov7goOwSkYc+OGvZWFqUPnewRQvq
|
||||
5jBJO54VoFHi42/qC9tjLFbp20ROHJRCmL8k+tweIprZsybHRokDyv/scwnMGKrU
|
||||
TiPa+3To0hyiOVNLNT/z7122Yqc8oTRcUzy/HzkVvAyUlunYXdOAfoqc+GTBDJMs
|
||||
DTllPMOhHr1bbOw5OTL7mrj2bEjUiS8lWZgfS5Y1zW2ATBrFwuXtTNPUb6OV1XSo
|
||||
/D4GyfYqQlm3H7tRgUYj3/3KxB+Tl0gVp80Jbfjc7i/+upEDblgip0Pyu79ufEJX
|
||||
h2GOfxO0p+MUYjE2uU3Pp5mJhgcldMDYwZkcV1qbWeA2OHKZdU3w5e4lhK4qnPmT
|
||||
pkNVIouYZDekr5vGuLvjkoPPGZZ6wxELUEUYNnXSpxreO1qhG5OvSf9XVlbk126n
|
||||
XxqlJ7lu8GdBSpTDmpA0IqYC13MooAvw4wFBkHfUbzCqEQPrYCST9K+awwnYvgmH
|
||||
FMo0X8Lg6lMnATc9R/P5UyQWIQTvbiht2oXqKkun3mhOLG6HkymCkFKBD/oC/XsW
|
||||
iT9qNt/GIEJ5pluWy+8fM/tVwx1r+B4L/efpVlkrlHGmNVpv456Hjcdm8te4k/+0
|
||||
sg+CzGf4iHjjoM7UCdBmx0IE9hy+3Jg2AG9i/IWDbhYHbGuyAxJQ1/Ft1xQyAzcL
|
||||
wJb+zEhVQwV1Q68vGSKIbbZlNwDKFUUJ/vWQZGHg2wZ5SUdMYh8OrKuT/pvsiL86
|
||||
8GuPvC+M9NupoWmQ5g+qouS/o+3JDqf5sXtu7ADDw9MdVr8TxjY6jBkHKlNNCvCG
|
||||
g+mPCTbZmMKcH8jKnTYJ2fGuBlAowJ58LN2ugkqQwRAWLQ9CDYPR7YEHnMYek7OM
|
||||
grPndB30hNFwDDjCYUeNHym+vVSF0mluEoSfh3slbmJ5RoNPlH3XPOnvt3fjlOBT
|
||||
oCG1EhQPqkC6R45eBWsbZ1URbNxIoyuAm5CQAayhdIn+gqw8Qd03VtmqAbgCaveU
|
||||
jdT4XSDORB2RZG/cFffxUAmx8TFHCp1QOKVPgx3ftGllMhMxcaLKNXhCBpTL7jpb
|
||||
c/ZSMmQg0uBBLIE5D2ETyJ9z0SgQH1KUI9lO5D/hn2d7dn2zb0cjdVoqngSsGCXE
|
||||
iRqLBnv3TVS6BHAqfSic5NL3PlFP4+QGmXuxT0EK5civrleGQm5SpzTqQ9iy1Ogw
|
||||
JOqh3i5cxX4RPcsHaqgZrHlY2oxvCncEb8RjDMLDlQQYAQoACQUCVI6wVgIbAgJA
|
||||
CRBOLG6HkymCkMFdIAQZAQoABgUCVI6wVgAKCRAuGsaO1AgU4JKjEACXrUoJM4is
|
||||
kF/tRCc67bl/QWVy4RDQlVhcCfnUAnuIr76f3EjuYqrD6c8QacbTFzggTwttYXx0
|
||||
9Mf3A388T/jZHV/9HkPG3cxpgpNmq/YiQhxCVvdvHo/IGkpS9UMt4f7hFjsL7dlW
|
||||
157ys8OQ8cgRSQ0A/FEeMvzE6uni7cYZ4lfLkrS896o0layi/uCg7BKRhz44a9lY
|
||||
WpQ+d7BFC+rmMEk7nhWgUeLjb+oL22MsVunbRE4clEKYvyT63B4imtmzJsdGiQPK
|
||||
/+xzCcwYqtROI9r7dOjSHKI5U0s1P/PvXbZipzyhNFxTPL8fORW8DJSW6dhd04B+
|
||||
ipz4ZMEMkywNOWU8w6EevVts7Dk5MvuauPZsSNSJLyVZmB9LljXNbYBMGsXC5e1M
|
||||
09Rvo5XVdKj8PgbJ9ipCWbcfu1GBRiPf/crEH5OXSBWnzQlt+NzuL/66kQNuWCKn
|
||||
Q/K7v258QleHYY5/E7Sn4xRiMTa5Tc+nmYmGByV0wNjBmRxXWptZ4DY4cpl1TfDl
|
||||
7iWEriqc+ZOmQ1Uii5hkN6Svm8a4u+OSg88ZlnrDEQtQRRg2ddKnGt47WqEbk69J
|
||||
/1dWVuTXbqdfGqUnuW7wZ0FKlMOakDQipgLXcyigC/DjAUGQd9RvMKoRA+tgJJP0
|
||||
r5rDCdi+CYcUyjRfwuDqUycBNz1H8/lTJBYhBO9uKG3aheoqS6feaE4sboeTKYKQ
|
||||
T40P/0U05rE5EiwLnCDQ14v+ynBUO17UU0SfAHf8NGARpAYqTwr2Ge521qH2CTS+
|
||||
NzdDdMvjpzr9io3voj8NPgDGpp3W8BDRc0Y7Zm+QEMLMwu4aGEaiVLmhpABDYSen
|
||||
TQ+ybxnIPT+buUMru+yTueWAX8vBFFCWqYS+WU1t52KUBP67f3amkQP8Jn43+X0P
|
||||
AUzztD+j9yvauIeZlT53AArAJgPnAtPIo7VWdHFaoDfIfGcloJRqR2AnWz834xN9
|
||||
Gw138M5lESumwQjkAJx7WG/UejfzGrrJFGEO71SQpXzS5ETkhrt3B45qeV6Cy/2A
|
||||
zx+DNwL+hE0q5RqPRSsTnKoqjY3n9AQoHnp9txlFkYEVqlyM0EOv3doW1toQAmNw
|
||||
1ZtA0kDNpzHBndFyrLT7Ptnfbao8fk3rh9TP0rVYMuXToIgQ3jDcuTaQHuZey9qS
|
||||
akbsMiC5skH2kWBjymE0G9gk4LRX/OpLu0E9/Yju26s4m01H08AkOS9rpOCo6D37
|
||||
fpi1mIhWOTwhDcbVYC6ePat6UV4UzLF9dv9W/sHupzAKCFYNvzxHN+ZNE1z/h9oa
|
||||
1u80x2zGADaJalDEXnM23xlh942USzrDsZScTmISgJPAmJ+Pmnf7SoqFTbofJrdB
|
||||
HyzUoyJSpIiUgmecnfuov0JigIbIb0xzHL7lwshGXNDgV3c8zsFNBFSOr/sBEADK
|
||||
ozhKT/c1dbHuIf4H3kigdq6VsvNGlDKJQakbTJuMKxVRc4nu4j2MUhgawlzvNQWi
|
||||
UEf5CC5X/BqU5wdL1ybhhFdxsXgkCLeFpxim1d+FIf0vBv9XdB+Z5Dv4w70Cemw4
|
||||
qM2HiXyaKltwEyc0U7ZN8w+PWmp56M+9yDgYwWn8vi7GtbAEugaF9c0jvlmK5C0l
|
||||
6XKULMr+CstYRdMyC1A6yhe3avWu7uUQXmwPLUj3mwzyZSYU0sT9Kw2LmJ+wOVJZ
|
||||
SgxIfGFv9CRAzrxl4IZn22s8FYonxU/9Dy7vd2RB2E9zRx/hnf9ksvThcga9bCV9
|
||||
jEa00rLV1MTI2iqsLdo/hOhFMYDF/kT0lSakck1ROsnUhImMqbXHXbQXmqTErblW
|
||||
ZbHSupdx+iM2OuFQhnhcMl2NRx1DNCqZNZ4h5vO/2yfGZjkJig1bAKZY9JB6FrX9
|
||||
8Yg1bS1ViTME1U3yAmQexaOX645oluq/ZFG4CJt2uizbe/Xr+h+7k20Y/goMO3Qb
|
||||
28j/gzrcoUVmIEtttBQFBUb4y8/UdEPKw19yWFyMJtBRKDAFb6fwTx/60DGaX/uI
|
||||
/mh2bt1nCyH1uOTpO7vAveLxRnMvTZNVeY59SbhWvyg9+LxJV5DOGhYN/rMwJkSi
|
||||
DFKxKAZtZZsBu5zToUiZ/04YsBDYVqEBDJd6tW3UFwARAQABwsObBBgBCgAPAhsC
|
||||
BQJV3aG7BQkFEViwAkAJEE4sboeTKYKQwV0gBBkBCgAGBQJUjq/7AAoJEHAXrc72
|
||||
XCA2yeoQAMW4rlCXUIC3QC89LzfJSQQ4cePGHakrzp9gDAA4+vhE7wt6FYadVeL4
|
||||
giOAUMo7l7htAL9rFebntzipnghRD54hwN/rO47dJJroZwyKV1/JBxdMFbawiOKD
|
||||
8iZJ1M+wuw7JbCKmurV88LIBFilfM1rhOK/itPKk/Rg3OE9KeOsm0ASZwGb9fSUf
|
||||
f1yf883BqHIG6Mae4lOfBhwIzNckw/4OePbke+eY9/LAR2RhKUVAT+O3Mshf2lxK
|
||||
x0Vxm66BlCXJh6Y+dNlGJZMnHUt3qcUZ7mQY/RtqMJ3wBslnCOa0hgeFwW43TKt5
|
||||
rTMMzCt5VoEfrPWiJK16OzzUKysLdzW+RiP5Vnkgjxg1fEzEKe/xr68+RBZou3Rr
|
||||
LOC+mXkRrKKEuZ0wOBmkFHiGl7zSXO/4FFUapNfzeDr4sn8yWTAbQPM/+e34MUzk
|
||||
aUKuKcHWxjcmFfOMfmDk1vtcnuuXOZ3gaOf1viGdI2ulcdrIYsdBH186GGF9FtKw
|
||||
YdQswxiIaOXnOCAwTfrD8lztVpDIGWBaNU940cdZOOqpXPCHNfgm8jZH/mZrO2UU
|
||||
qlOGaW8ehFIMhIEzk0HZHCO0DCVZ9EXf2dfv16rHpLO0D/f693MNLFFYYSOTgP7a
|
||||
BYwxrbJ0nL0gymxCvszWzd39jfc6ZhVeOn3Jy4LYYKsyAwT3mUXGFiEE724obdqF
|
||||
6ipLp95oTixuh5MpgpBpSRAAqYhRv2QkAkRDVG+AqgUjnI5EpYYRGXPZM5u88abd
|
||||
lLVQeH2rWH+LDL/8MisnfmJWv8Bi1igch0W8nxDUdtjIjnGcaLpVwIo4DZX76TLn
|
||||
r/j2O1IHvSDiPdEIFgSFsPA5CMCOUSmt7rWmk3wYxMGSBhBWfZgNjj61e5Z4+WtS
|
||||
dsY8BLmLdXSo3tur5msxt1+8A/OTvcA/yy84B6cOvPwAkELklyC4/AwGuRTucg68
|
||||
tIA960DIzJIIF46dJ1gtd76YWonFHoIxNhRX/wdy+7Ca0nyhlXIuvOyOL/7+K3VX
|
||||
S/lUdFQA6BX0u7WOF0086Gs7DLALF09Gqmu1w44u63CPzVFGfO4fNCOf8vFKLUof
|
||||
cDpkK7QFvX63VTAXxUNkkq+0KKcpyTiVQztmv2CPmQUu9G/W1NexLDaxIwbKQ+FT
|
||||
+dSVKNGSUT3rDgDv9bycd4PQhz9E4tuG+TRxLizFqjzn7Azk+fT0vbkmRA+83Dmy
|
||||
vyx18LjpEb1pWoS8mLVHv4mfNzOK4NuAfUWxT/WvR3z7aXzvhxWlNrfLYOSp4Xss
|
||||
vPFBpUS5qNB5yUiKRJd4iMT9ouGG7XkNlUqyyfJ9aEfdCiq0yB8dqIb9aF0OUx+G
|
||||
i9wStWqNboXn1k6O6pSrTfOEt/uKQgAlVRn7o7TnRHfFjOkos9M2F0ARYaGcR1QJ
|
||||
yQvCw5UEGAEKAAkFAlSOr/sCGwICQAkQTixuh5MpgpDBXSAEGQEKAAYFAlSOr/sA
|
||||
CgkQcBetzvZcIDbJ6hAAxbiuUJdQgLdALz0vN8lJBDhx48YdqSvOn2AMADj6+ETv
|
||||
C3oVhp1V4viCI4BQyjuXuG0Av2sV5ue3OKmeCFEPniHA3+s7jt0kmuhnDIpXX8kH
|
||||
F0wVtrCI4oPyJknUz7C7DslsIqa6tXzwsgEWKV8zWuE4r+K08qT9GDc4T0p46ybQ
|
||||
BJnAZv19JR9/XJ/zzcGocgboxp7iU58GHAjM1yTD/g549uR755j38sBHZGEpRUBP
|
||||
47cyyF/aXErHRXGbroGUJcmHpj502UYlkycdS3epxRnuZBj9G2ownfAGyWcI5rSG
|
||||
B4XBbjdMq3mtMwzMK3lWgR+s9aIkrXo7PNQrKwt3Nb5GI/lWeSCPGDV8TMQp7/Gv
|
||||
rz5EFmi7dGss4L6ZeRGsooS5nTA4GaQUeIaXvNJc7/gUVRqk1/N4OviyfzJZMBtA
|
||||
8z/57fgxTORpQq4pwdbGNyYV84x+YOTW+1ye65c5neBo5/W+IZ0ja6Vx2shix0Ef
|
||||
XzoYYX0W0rBh1CzDGIho5ec4IDBN+sPyXO1WkMgZYFo1T3jRx1k46qlc8Ic1+Cby
|
||||
Nkf+Zms7ZRSqU4Zpbx6EUgyEgTOTQdkcI7QMJVn0Rd/Z1+/Xqseks7QP9/r3cw0s
|
||||
UVhhI5OA/toFjDGtsnScvSDKbEK+zNbN3f2N9zpmFV46fcnLgthgqzIDBPeZRcYW
|
||||
IQTvbiht2oXqKkun3mhOLG6HkymCkFXcD/4ivzx5WOGHMhmfVy1jjSrXkfy4J841
|
||||
HGkyTBLB4vD+ii9LtM0mFivy+6VVJVcNaMdQGel5LmSubAFifHjUIqXrEazj3N2P
|
||||
ME4Xno5XjcbQBEgp7YSLtmMY2JGMIyUzF8D70g2kZ/Q8+mmOLMO80zjiOdRUyIvq
|
||||
FWjIS/gCgIZQR0bKxjhk7YeAM9oXc5mwsjc63811xt8BC34CT2m1S6bcBQiX262f
|
||||
yjowb99GNGhD41tsPndKeGil2aTeMiL5fpdC7RdQ3fSQ49xZY+O5XoRqQYzI8U4I
|
||||
HS83C55TZUd9rMZZ85iINWBRQC06iEAqKe1iRm8dHuomeD40j/XyWsYTd+7smf+y
|
||||
psO2EhQjieX/IH5f3eueJ3WBDFLUGV/qeMJOKnQXWZGxoMS3281T7KwEEuibqh+B
|
||||
HK4GNl8y1HHxqSImVM2Jly96fkELgQVB8OEZGzWOtuvsy4OiPK6t+tpgR/E+aU2B
|
||||
bI3mpfEYlhslME5+bgJAr/lnBQX4ABHhT/MdDdojqRXP2YEEgKjz0Lr4oSoQEjNG
|
||||
BAc0YeUO8wY93NfO4bkk1VZ98L4kxYD/JoLrtIpdcjw7z5fAp7kUBawtr2h3A4XK
|
||||
Rl5XiKS4HasCbDwONhRrcyJpwYsW0o6U5W8EhzEyNFTV42i1hxO4NmW+Nr9yQlnL
|
||||
xfFpmyjRwFlQl87BTQRUjrDqARAA5J2r83RPx1vw9O61+QFFW2R1Wm+G6aQpZuMG
|
||||
g3gLADwFVlIB92A0ITvi3I/6Ts9rzTZ5AFQTp/SPisPwTuiEY7rW+jdksggw8YJA
|
||||
4u285OE0pTxkGcI91RuTqaxs+ad+H0qHPffMaSOjPOve4uREMqqDAK7bayDEwxr5
|
||||
LOzbIGl6NpviZJGCU2ASCbWwvGghQFfYPO5XqOHj4neMIJI5JQNKXwcXcQtoF5s2
|
||||
jZV6AZ286pM4o8tmn7x3//x7OL9brbBSHEmW0mwU9ErDesWQh4aBJSiDmihf9X1X
|
||||
1g7RSwOYI7dWDLmzhvJy2AK4RvFrZk9pvCtGClpdSA4a79rk8M7W956wImEpsC4e
|
||||
/CCe1o4Oljo+Oac0D/1DOsqFSkBiniN5IA3G13a0sUz6f8fArifzLGcFwzZ2pzZZ
|
||||
5t9kMc3M3KoIa/7cK6BOi6pb3tyAk9tLsiQQwBWbbPKAx7PHfLenttULnTZLGQay
|
||||
ctzD0f6psFKDTW/yGIpW0j/uMVFDEAdKalA62T5juMCnGjHXbcEY60kqOzGRSjUY
|
||||
ST6sikKS6f4OTXc2VjDgM20go9AGeDH44E2L1ctLSzlmyV87CTh/FN0mWY0AYuZe
|
||||
+xXKg4Cwmj4q3scfkoookvthSsTtrrnwb/pm6JjL1dtYjjS0ukqsCsWh1M431br4
|
||||
B6obVtEAEQEAAcLBdgQoAQoACQUCVd2g7QIdAgAhCRBOLG6HkymCkBYhBO9uKG3a
|
||||
heoqS6feaE4sboeTKYKQld0QAIsyTVuD3SmvdBIgFS+t7U5c+Pi+GoTxTV2HgeVl
|
||||
7f9n2UvewfJK97hT+xpxo+6I4+P/vcer01ohYLh8N2RqSUZFnqB0S4HvngMZfQF1
|
||||
7TA3XiZNIc6ndeDY9t3kZrLKKngLUJtc1z75pgtMm35uwsZwCHb2TQiE7eCdIOmz
|
||||
Ksnq0duedPmWOpgJTf8euSTX5ht1yd16OlighsvIWjo2lq3EzPQUdcDPNuuq2giW
|
||||
pI2Vy4TiyVjEQL3GCMKQnSigE3vgHKA0+TfIECZoAyY10X7LRiz5CZJrvYZqPQHy
|
||||
462YbakRnsZmj56m9nkEr5c7gA2brsvRsiMefrfOauzh7O+6cmAZvPLKNi5CW8IS
|
||||
+dVzSVus+o9Y1/bm2y4GZiZQJMfJzZlTFj3sSh69mhPpx5EZrkvXtkyCu6OXLrZb
|
||||
dDLiEYwc+Ui6wepu0x+eN/2Jvof71sY10hhTnjH1cenvM6TQ7uFpkT5kfEJR165z
|
||||
oZVrsBWN5CBmhFrViMWpSs9V8mYo2lp2fgCGwJ3y4VT043MGpMthJtDQ0Kgi8JOI
|
||||
8LllAcs8FbqzSZMAMJYV1Yd3AMlfH4sA16KSOEc8q4HLlvsi8grF0xdBYKeys2JH
|
||||
y6OTPThpSrz1by5gs7vqEYn9hBlQXRo+/uuaVeBN/2E3c7MPBHWAWSin3rcQE2PW
|
||||
SXATwsOVBBgBCgAJBQJUjrDqAhsCAkAJEE4sboeTKYKQwV0gBBkBCgAGBQJUjrDq
|
||||
AAoJEC0ACYhYmDmjtlEP/3JlkkpH24Ej8AE+XBZwt4qanBb2Pi/vzg3Ke9t7pyMH
|
||||
7JWkg7LFGSpQTIG4/HOfRGahG68SQNKyPS+MUwhtqQrrEfOGnmXf+TS2F1TqKVGI
|
||||
sFpNiNSi5zW4Q2GBlhwDTsPGOsnAAjlsRjkgwwegq6UEDJYTz3X1tkbY2iFH4oSr
|
||||
/hdpgOeI4Z2rHQmgJNGhpHSZidOP9gYZMFqQXqRaZevljMHtaANJTQoP3l4aMVvA
|
||||
U+RMSusNJc6dv+D/cGY1bQGKTjlPUnR4+4NV6R+r2r2qUJXM/DX8Pgcl+mSyK9WG
|
||||
kKTrDq/xAXI8sNNiwj4j/rSKPwcOKPI5vPjiLf6p6xKpcASwRZUDuTYrMug6+7sq
|
||||
hAC7+zBmxMSZt945wksh0eaMkQxSOAWIqdj3RWeVgFCmb5aufpYfmHdwX/LgtpYz
|
||||
GQ+hHeqwKdNDODFi581Jqz4P+Cc1nO0vleXQWRIRFiXrD1PE+8a/LrRu29i8RV2M
|
||||
2UauY0sq+tljzGyx85UUyHc7AXTbAZyS4KOTwmx8CEMRl3iaB1nfmEx7TDkQRd7g
|
||||
qQvwIByoqFOedYqXMdXvGhcqbJDMWWUR5ZUzm8CnsXjvHNn09VfRr0JxJ+e9pdEK
|
||||
WPc893bgfN7PNK6R55ejY0aMBGz7qgSqSyMcN2u1qN9fakUA2DhZvU16ztAg2fDG
|
||||
FiEE724obdqF6ipLp95oTixuh5MpgpA7+w/8DSi8KSDRNxrMbhME1R/CcSNoBQrQ
|
||||
OoGSJLdBhW9QSEjMP+6hATkenbGmrVxl1mICzHuqXgr+JW92DbPwR8z6A1k5SO8V
|
||||
1xOesX7zgt2sy5RvfNZe9mmiaa0CxeY6YG0NaqF2+OA5vvpHXh3vwGOcN481rTLd
|
||||
KF4lQhzvpzdHlYUTYwtf3MIwAOJdzsZB+jGJphRh6XS+mTABtAigXFAP901sRfiL
|
||||
+41Zo1nQtm7sjBVhHVEISObFZLTdXwvTn5ufw+3rmY5X5n8A2QK1V9j4y0DYFk1P
|
||||
4dW9OoanWU/kLRyqPDsPjr8Ey0UeZyEur4HXBdFucy1Fn2/EtUFTYWaPOqyVl0/I
|
||||
lKTlfGY0566l/n48kwOx/P4PHIVr36tE6XHPRB1d6ui6fpzjm2FgC7odjna2gD9I
|
||||
hHAeLfjG1wvXXHcFCeZHdf1B0nicivxYAGb0Nc8ICl89WMfKp+/LMzD08Alzz26M
|
||||
fmglv6zJx2J0b8puMEtTiM4ESrDVrMxewibZ4cI9e1g86WXGPlIZ0ApicFlr69bT
|
||||
IPzIYNmYwWqab2tqm0MQVRpNDWMIkWJ/r3TTmNN+Fqv827Fo7qR8zjPVi8DyoKmF
|
||||
zfgya2ZoE7od5bGg7lcM7UhzEPfwZUMqKaawlrnzqy1sGLJi0QZErUhHo3tU9sHY
|
||||
qAtUENvs4LC7dEE=
|
||||
=Jqal
|
||||
-----END PGP PUBLIC KEY BLOCK-----
|
||||
|
|
@ -35,6 +35,9 @@
|
|||
<a class="settings-bar-btn" href="{{ url_for('settings_endpoint.settings') }}">
|
||||
<img src="{{ url_for('static', filename='img/settings-status-bar.svg') }}" style="width: 22px;"/>
|
||||
</a>
|
||||
<a class="settings-bar-btn" href="{{ url_for('about') }}" title="About and help">
|
||||
<img src="{{ url_for('static', filename='img/icon.png') }}" style="width: 22px;"/>
|
||||
</a>
|
||||
{% if specter.config.auth.method != "none" %}
|
||||
<a class="settings-bar-btn" href="{{ url_for('auth_endpoint.logout') }}">
|
||||
<img src="{{ url_for('static', filename='img/logout-status-bar.svg') }}" style="width: 22px;"/>
|
||||
|
|
@ -69,7 +72,11 @@
|
|||
<img src="{{ url_for('static', filename='img/ghost.png') }}" width="40px" style="vertical-align: top; margin-left: 20px;"/>
|
||||
</h1>
|
||||
<p style="font-size: 1.2em; font-weight: bolder;">Specter Desktop is a convenient wallet interface to better use your Bitcoin Core node. It’s good for singlesig wallets, but comes with a special focus on multisignature setups for different hardware wallets and air-gapped signing devices.</p>
|
||||
<br><br><br>
|
||||
<br><br>
|
||||
{% if not specter.chain %}
|
||||
<a href="{{ url_for('node_setup_wizard') }}" class="btn action centered" style="max-width: 200px;">Get started!</a>
|
||||
<br><br>
|
||||
{% else %}
|
||||
<h2 style="font-size: 1.6em;">Getting Started</h2><br>
|
||||
<table>
|
||||
<tbody>
|
||||
|
|
@ -78,8 +85,8 @@
|
|||
<img src="{{ url_for('static', filename='img/checkbox-' + ('un' if not specter.chain else '') + 'tick.svg') }}" width="30px">
|
||||
</td>
|
||||
<td style="text-align: left;">
|
||||
<a href="{{ url_for('settings_endpoint.settings') }}">
|
||||
<h2>Connect Specter to your Bitcoin Core node.</h2>
|
||||
<a href="{{ url_for('node_setup_wizard') }}">
|
||||
<h2>Connect Specter with Bitcoin Core node.</h2>
|
||||
</a>
|
||||
</td>
|
||||
<td style="text-align: left;">
|
||||
|
|
@ -175,6 +182,7 @@
|
|||
</tbody>
|
||||
</table>
|
||||
<br>
|
||||
{% endif %}
|
||||
Having issues? Try reading <a href="https://github.com/cryptoadvance/specter-desktop/blob/master/docs/faq.md" target="_blank" style="color: #fff; font-size: 1.05em; font-weight: bolder;">the FAQ</a><br><br>
|
||||
Still having issues?<br>
|
||||
Feel free to <a href="https://github.com/cryptoadvance/specter-desktop/issues/new" target="_blank" style="color: #fff;">open an issue on the GitHub</a> or ask in the <a href="https://t.me/spectersupport" target="_blank" style="color: #fff;">Specter Community telegram chat</a>
|
||||
|
|
|
|||
|
|
@ -228,9 +228,9 @@
|
|||
if (jsonResponse.label) {
|
||||
self.label.innerText = jsonResponse.label;
|
||||
}
|
||||
return
|
||||
};
|
||||
showError('Failed to fetch address label data.')
|
||||
return;
|
||||
}
|
||||
showError('Failed to fetch address label data.');
|
||||
} catch(e) {
|
||||
console.log("Caught error: ", e);
|
||||
showError(e);
|
||||
|
|
|
|||
365
src/cryptoadvance/specter/templates/node_setup_wizard.jinja
Normal file
365
src/cryptoadvance/specter/templates/node_setup_wizard.jinja
Normal file
|
|
@ -0,0 +1,365 @@
|
|||
{% extends "base.jinja" %}
|
||||
|
||||
{% block main %}
|
||||
<style>
|
||||
.wizard-btn {
|
||||
width: 200px;
|
||||
min-width: 200px;
|
||||
max-width: 200px;
|
||||
height: 36px;
|
||||
margin: auto;
|
||||
}
|
||||
.helper-btn {
|
||||
text-decoration: underline;
|
||||
margin: 10px 50px;
|
||||
}
|
||||
</style>
|
||||
{% from "components/bitcoin_svg.jinja" import bitcoin_svg %}
|
||||
<div class="card center hidden" id="step0">
|
||||
<h1>Specter Quickstart!</h1>
|
||||
<h2>Get up and running with Specter in just a few clicks!</h2><br>
|
||||
<img style="width: 50px;" src="{{ url_for('static', filename='img/tor.svg') }}"/>
|
||||
{{ bitcoin_svg('main', 50) }}
|
||||
<img src="{{ url_for('static', filename='img/icon.png') }}" style="width: 50px;"/>
|
||||
<br><br>
|
||||
<p style="max-width: 400px; margin: auto;">
|
||||
Here, we'll take you through the process of setting up Specter with your own Bitcoin Core node and Tor.<br><br>
|
||||
Everything is completely automated, but you make choices to customize your setup to fit your needs.
|
||||
</p>
|
||||
<br><br>
|
||||
<div class="row">
|
||||
<button type="button" class="btn wizard-btn action" onclick="showStep(1)">Get Started!</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card center hidden" id="step1">
|
||||
<h1><img src="{{ url_for('static', filename='img/icon.png') }}" style="width: 60px;"/> <img src="{{ url_for('static', filename='img/arrow_right.svg') }}" style="width: 60px;"/> <img style="width: 60px;" src="{{ url_for('static', filename='img/tor.svg') }}"/><br>
|
||||
<br>Download and setup Tor daemon</h1>
|
||||
<h2 style="color: #ccc;">(optional, recommended)</h2>
|
||||
<p>This should take between a few seconds to a few minutes, depending on your internet connection...</p>
|
||||
<p class="hidden warning" id="step1-helper-text">Here, Specter will download the Tor daemon software, verify signatures to ensure download files can be trusted, and start your Tor daemon to allow you to make calls over the Tor network.</p>
|
||||
<div class="row center">
|
||||
<p class="explorer-link" id="step1-helper" onclick="toggleHelper('step1')">Unsure? Click here for more info!</p>
|
||||
<a class="explorer-link hidden" id="step1-support" href="https://t.me/spectersupport" target="_blank">Still need help?</a>
|
||||
</div>
|
||||
<br><br>
|
||||
<div class="row">
|
||||
<button type="button" class="btn wizard-btn" onclick="showStep(2)">Skip</button>
|
||||
<button type="button" class="btn wizard-btn action" id="setup-tor-button" onclick="setupTorDaemon()">Setup Tor!</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card center hidden" id="step2">
|
||||
<h1><img src="{{ url_for('static', filename='img/icon.png') }}" style="width: 60px;"/> <img src="{{ url_for('static', filename='img/arrow_right.svg') }}" style="width: 60px;"/> {{ bitcoin_svg('main', 60) }}<br>
|
||||
<br>Would you like to setup a new Bitcoin node or connect to an existing one?</h1>
|
||||
<p class="hidden warning" id="step2-helper-text">If you have an existing Bitcoin Core node you would like to use, either running locally on this computer or somewhere else, like on Umbrel, myNode, or RaspiBlitz, you can just connect and use it.<br><br>
|
||||
Don't have a node? Specter can help you setup a new one! This guide will help you get it up and running!</p>
|
||||
<div class="row center">
|
||||
<p class="explorer-link" id="step2-helper" onclick="toggleHelper('step2')">Unsure? Click here for more info!</p>
|
||||
<a class="explorer-link hidden" id="step2-support" href="https://t.me/spectersupport" target="_blank">Still need help?</a>
|
||||
</div>
|
||||
<br><br>
|
||||
<div class="row">
|
||||
<a href="{{url_for('settings_endpoint.bitcoin_core')}}" class="btn wizard-btn">Connect existing node</a>
|
||||
<button type="button" class="btn wizard-btn action" onclick="showStep(3)">Setup a new node</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card center hidden" id="step3">
|
||||
<h1><img src="{{ url_for('static', filename='img/icon.png') }}" style="width: 60px;"/> <img src="{{ url_for('static', filename='img/arrow_right.svg') }}" style="width: 60px;"/> {{ bitcoin_svg('main', 60) }}<br>
|
||||
<br>Download and setup Bitcoin Core</h1>
|
||||
<p>This should take between a few seconds to a few minutes, depending on your internet connection...</p>
|
||||
<p class="hidden warning" id="step3-helper-text">Here, Specter will download the Bitcoin Core release software, verify signatures to ensure download files can be trusted, and prepare basic configurations of your new Bitcoin Core node.</p>
|
||||
<div class="row center">
|
||||
<p class="explorer-link" id="step3-helper" onclick="toggleHelper('step3')">Unsure? Click here for more info!</p>
|
||||
<a class="explorer-link hidden" id="step3-support" href="https://t.me/spectersupport" target="_blank">Still need help?</a>
|
||||
</div><br>
|
||||
<span id="toggle_advanced_3" style="cursor: pointer;" onclick="toggleAdvanced(3)">Advanced ▶</span>
|
||||
<br>
|
||||
<br>
|
||||
<div id="advanced_settings_3" class="card hidden">
|
||||
<h2 style="text-decoration: underline;">Advanced configurations</h2><br>
|
||||
<p style="text-align: left;">
|
||||
Bitcoin Core data directory path:<br>
|
||||
<input type="text" id="bitcoin_core_datadir" name="bitcoin_core_datadir" type="text" value="{{ specter.config.rpc.datadir }}">
|
||||
</p>
|
||||
</div>
|
||||
<br><br>
|
||||
<div class="row">
|
||||
<button type="button" class="btn wizard-btn action" id="setup-bitcoind-button" onclick="setupBitcoind()">Start the Setup!</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card center hidden" id="step4">
|
||||
<h1><img src="{{ url_for('static', filename='img/icon.png') }}" style="width: 60px;"/> <img src="{{ url_for('static', filename='img/arrow_right.svg') }}" style="width: 60px;"/> {{ bitcoin_svg('main', 60) }}<br>
|
||||
<br>Configue your node</h1>
|
||||
<p>QuickSync?
|
||||
<label class="switch">
|
||||
<input type="checkbox" name="quicksync" id="quicksync" onchange="toggleNodeTypeSelect()" checked>
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
<tool-tip title="What is QuickSync?">
|
||||
QuickSync will set up your new node from a trusted state of the blockchain.<br><br>
|
||||
This means all block validation will be skipped until the most recent block in the snapshot.<br><br>
|
||||
Later blocks will be normally validated like with a regular full node.
|
||||
</tool-tip>
|
||||
</p>
|
||||
<div class="hidden" id="select-node-type-div">
|
||||
<br>
|
||||
<h1>Select node type:</h1>
|
||||
<div class="row">
|
||||
<label style="position: relative;">
|
||||
<input type="radio" name="node-type" id="node-type-archival" class="hidden" value="archival">
|
||||
<div class="small-card radio">
|
||||
<span style="font-size: 1.2em; font-weight: bold;">Archival Node</span><br>
|
||||
Requires about 400GB of available storage
|
||||
</div>
|
||||
</label>
|
||||
<label style="position: relative;">
|
||||
<input type="radio" name="node-type" class="hidden" value="pruned" checked>
|
||||
<div class="small-card radio">
|
||||
<span style="font-size: 1.2em; font-weight: bold;">Pruned Node</span><br>
|
||||
Requires about 10GB of available storage<br><br>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<p class="hidden warning" id="step4-helper-text">
|
||||
Now that you have Bitcoin Core installed, your next step will be to sync with the network.<br><br>
|
||||
Specter allows you to choose between normal syncing (IBD) - which will download and validate the entire history of the blockchain, or downloading a trusted snapshot of the current state of the network.<br><br>
|
||||
This snapshot is produced and signed by Specter team - which means you will be trusting it to get the present state of the blockchain, instead of verifying the history yourself.<br><br>
|
||||
It takes about 5GB to download and will setup a fully synced pruned node for you to use.
|
||||
You can also disable the QuickSync option, and choose between setting up an archival node (which keeps the entire history) or a pruned node.<br><br>
|
||||
<b>Please note: it is recommended to use a full archival node, or if storage is a constraint, a pruned node synced from scratch. The option for QuickSync is useful for getting started quickly, but is not recommended in the long term or when using Specter with large amounts.</b>
|
||||
</p>
|
||||
<div class="row center">
|
||||
<p class="explorer-link" id="step4-helper" onclick="toggleHelper('step4')">Unsure? Click here for more info!</p>
|
||||
<a class="explorer-link hidden" id="step4-support" href="https://t.me/spectersupport" target="_blank">Still need help?</a>
|
||||
</div>
|
||||
<br><br>
|
||||
<div class="row">
|
||||
<button type="button" class="btn wizard-btn action" id="setup-bitcoind-dir-button" onclick="setupBitcoindDirectory()">Start Syncing!</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card center hidden" id="step5">
|
||||
<img style="width: 100px;" src="{{ url_for('static', filename='img/party.png') }}"/><br><br>
|
||||
<h1 style="font-size: 1.8em;">Setup Completed Successfully!</h1>
|
||||
<p>Your new node is up and running!</p><br>
|
||||
<a href="{{url_for('about')}}" class="btn wizard-btn action">Done</a>
|
||||
</div>
|
||||
<br>
|
||||
<button type="button" class="btn hidden action" id="show-progress-details" style="max-height: 36px;" onclick="showPageOverlay('stage-progress-details');">View Progress</button>
|
||||
<h1 id="stage-progress-details" class="hidden" style="margin: auto;"></h1>
|
||||
<br><br>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let currentStep = parseInt('{{step}}');
|
||||
let torSetup = false;
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
showStep(currentStep);
|
||||
});
|
||||
|
||||
function showStep(step) {
|
||||
currentStep = step;
|
||||
document.getElementById('step0').classList.add('hidden');
|
||||
document.getElementById('step1').classList.add('hidden');
|
||||
document.getElementById('step2').classList.add('hidden');
|
||||
document.getElementById('step3').classList.add('hidden');
|
||||
document.getElementById('step4').classList.add('hidden');
|
||||
document.getElementById('step5').classList.add('hidden');
|
||||
document.getElementById(`step${step}`).classList.remove('hidden');
|
||||
}
|
||||
|
||||
function toggleHelper(step) {
|
||||
let helper = document.getElementById(step + "-helper");
|
||||
let helperText = document.getElementById(step + "-helper-text");
|
||||
let support = document.getElementById(step + "-support");
|
||||
if (helperText.classList.contains("hidden")) {
|
||||
helper.innerText = "Got it!";
|
||||
helperText.classList.remove("hidden");
|
||||
support.classList.remove("hidden");
|
||||
helper.classList.add("helper-btn");
|
||||
support.classList.add("helper-btn");
|
||||
} else {
|
||||
helper.innerText = "Unsure? Click here for more info!";
|
||||
helperText.classList.add("hidden");
|
||||
support.classList.add("hidden");
|
||||
helper.classList.remove("helper-btn");
|
||||
support.classList.remove("helper-btn");
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAdvanced(step) {
|
||||
let advancedButton = document.getElementById(`toggle_advanced_${step}`);
|
||||
let advancedSettings = document.getElementById(`advanced_settings_${step}`);
|
||||
if (advancedSettings.classList.contains("hidden")) {
|
||||
advancedSettings.classList.remove("hidden");
|
||||
advancedButton.innerHTML = 'Advanced ▼';
|
||||
} else {
|
||||
advancedSettings.classList.add("hidden");
|
||||
advancedButton.innerHTML = 'Advanced ▶';
|
||||
}
|
||||
}
|
||||
|
||||
function toggleNodeTypeSelect() {
|
||||
let nodeTypeDiv = document.getElementById('select-node-type-div');
|
||||
if (document.getElementById('quicksync').checked) {
|
||||
nodeTypeDiv.classList.add('hidden');
|
||||
} else {
|
||||
nodeTypeDiv.classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function setupTorDaemon() {
|
||||
torSetup = true;
|
||||
let url = "{{ url_for('setup_tor') }}"
|
||||
var formData = new FormData();
|
||||
formData.append('csrf_token', '{{ csrf_token() }}');
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}
|
||||
);
|
||||
if(response.status != 200){
|
||||
showError(await response.text());
|
||||
return;
|
||||
}
|
||||
const jsonResponse = await response.json();
|
||||
if ("success" in jsonResponse) {
|
||||
showNotification(jsonResponse.success);
|
||||
document.getElementById('setup-tor-button').classList.add('hidden');
|
||||
startProgressCheck();
|
||||
return;
|
||||
} if (jsonResponse.error == "tor is already installed") {
|
||||
showNotification('Tor already installed, skipped step.');
|
||||
showStep(++currentStep);
|
||||
return;
|
||||
}
|
||||
showError(jsonResponse.error, 4000);
|
||||
} catch(e) {
|
||||
showError("Failed to download Tor daemon...");
|
||||
showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function setupBitcoind() {
|
||||
torSetup = false;
|
||||
let url = "{{ url_for('setup_bitcoind') }}"
|
||||
var formData = new FormData();
|
||||
formData.append('csrf_token', '{{ csrf_token() }}');
|
||||
formData.append('bitcoin_core_datadir', document.getElementById('bitcoin_core_datadir').value);
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}
|
||||
);
|
||||
if(response.status != 200){
|
||||
showError(await response.text());
|
||||
return;
|
||||
}
|
||||
const jsonResponse = await response.json();
|
||||
if ("success" in jsonResponse) {
|
||||
showNotification(jsonResponse.success);
|
||||
document.getElementById('setup-bitcoind-button').classList.add('hidden');
|
||||
startProgressCheck();
|
||||
return;
|
||||
} if (jsonResponse.error == "bitcoind already installed") {
|
||||
showNotification('Bitcoin Core already installed, skipped step.');
|
||||
showStep(++currentStep);
|
||||
return;
|
||||
}
|
||||
showError(jsonResponse.error, 4000);
|
||||
} catch(e) {
|
||||
showError("Failed to download Bitcoin Core...");
|
||||
showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
async function setupBitcoindDirectory() {
|
||||
torSetup = false;
|
||||
let url = "{{ url_for('setup_bitcoind_directory') }}"
|
||||
var formData = new FormData();
|
||||
formData.append('csrf_token', '{{ csrf_token() }}');
|
||||
formData.append('quicksync', document.getElementById('quicksync').checked);
|
||||
formData.append('nodetype', document.getElementById('node-type-archival').checked ? 'archival' : 'pruned');
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}
|
||||
);
|
||||
if(response.status != 200){
|
||||
showError(await response.text());
|
||||
return;
|
||||
}
|
||||
const jsonResponse = await response.json();
|
||||
if ("success" in jsonResponse) {
|
||||
showNotification(jsonResponse.success);
|
||||
document.getElementById('setup-bitcoind-dir-button').classList.add('hidden');
|
||||
startProgressCheck();
|
||||
return;
|
||||
}
|
||||
showError(jsonResponse.error, 4000);
|
||||
} catch(e) {
|
||||
showError("Failed to start Bitcoin Core...");
|
||||
showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
function startProgressCheck() {
|
||||
async function fetchStageProgress() {
|
||||
try {
|
||||
let url = "{{ url_for('get_node_setup_status') }}"
|
||||
|
||||
if (torSetup) {
|
||||
url = "{{ url_for('get_tor_setup_status') }}"
|
||||
}
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
);
|
||||
let result = await response.json();
|
||||
if (result.error) {
|
||||
showError('Encountered an error:' + result.error);
|
||||
document.getElementById('stage-progress-details').classList.add('hidden');
|
||||
hidePageOverlay('stage-progress-details');
|
||||
return;
|
||||
}
|
||||
let stage = result.stage;
|
||||
let progress = parseFloat(result.stage_progress);
|
||||
if (progress == -1) {
|
||||
showNotification('Finished successfully');
|
||||
showStep(++currentStep);
|
||||
document.getElementById('stage-progress-details').classList.add('hidden');
|
||||
document.getElementById('show-progress-details').classList.add('hidden');
|
||||
hidePageOverlay('stage-progress-details');
|
||||
return;
|
||||
}
|
||||
document.getElementById('stage-progress-details').innerHTML = `${stage}<br>`;
|
||||
if (progress > 0) {
|
||||
document.getElementById('stage-progress-details').innerHTML += `<br><br><b style="font-size:0.8em;">Progress: ${progress}%</b>`;
|
||||
}
|
||||
setTimeout(fetchStageProgress, 1000);
|
||||
} catch(e) {
|
||||
console.log('Caught error:', e);
|
||||
return { success: false, error: e };
|
||||
}
|
||||
}
|
||||
document.getElementById('stage-progress-details').classList.remove('hidden');
|
||||
document.getElementById('show-progress-details').classList.remove('hidden');
|
||||
showPageOverlay('stage-progress-details');
|
||||
document.getElementById('stage-progress-details').innerHTML = 'Stating up...';
|
||||
|
||||
setTimeout(fetchStageProgress, 1000);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
@ -7,7 +7,48 @@
|
|||
<h1 id="title" class="settings-title">Bitcoin Core settings - Specter Desktop {{ current_version }}</h1>
|
||||
{% from 'settings/components/settings_menu.jinja' import settings_menu %}
|
||||
{{ settings_menu('bitcoin_core', current_user) }}
|
||||
<div class="card" style="margin: 20px auto 120px;">
|
||||
<nav class="row">
|
||||
<button type="button" id="external_node_view_btn" class="btn radio left checked" onclick="setNodeTypeView('external')"> Existing Node </button>
|
||||
<button type="button" id="internal_node_view_btn" class="btn radio right" onclick="setNodeTypeView('internal')"> Built In Node </button>
|
||||
</nav>
|
||||
|
||||
<div id="internal_node_setup_view" class="card" style="margin: 20px auto 120px; text-align: center;">
|
||||
{% if external_node %}
|
||||
<button type="submit" class="btn centered" name="action" value="useinternal">Switch to built-in node</button>
|
||||
<br><br>
|
||||
{% endif %}
|
||||
{% if bitcoind_exists %}
|
||||
<h2>Built in Bitcoin Node Status: {% if is_running %}Running{% else %}Down{% endif %}</h2><br>
|
||||
<span class="note" style="font-size: 1.3em;">Bitcoin Core Version: {{specter.config.bitcoind_internal_version}}</span>
|
||||
<br><br>
|
||||
{% if not external_node %}
|
||||
{% if is_running %}
|
||||
<button type="submit" class="btn centered" name="action" value="stopbitcoind">Stop Bitcoin Core</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn centered" name="action" value="startbitcoind">Start Bitcoin Core</button>
|
||||
<p class="warning">
|
||||
If this is your first time starting the node, and you haven't used the QuickSync, your node will start with Initial Block Downloading (IBD) to sync with the network.<br>
|
||||
This process of IBD syncing may take several days. If you'd like the QuickSync option, please click on it before starting your node, or if it's running, stop it and the click the QuickSync.<br>
|
||||
<b>(Warning: QuickSync will override any existing data of your Bitcoin Node!)</b>
|
||||
</p>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<p>Please switch node type to start the built in node.</p>
|
||||
{% endif %}
|
||||
<br><br>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<h1>Setup Bitcoin Core Node</h1><br>
|
||||
<a href="{{url_for('node_setup_wizard', step=3)}}" class="btn wizard-btn" style="width: 200px; margin: auto;">Setup new Bitcoin node</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div id="external_node_setup_view" class="card" style="margin: 20px auto 120px;">
|
||||
{% if not external_node %}
|
||||
<button type="submit" class="btn centered" name="action" value="useexternal">Switch to external node</button>
|
||||
<br><br>
|
||||
{% endif %}
|
||||
<h2>Bitcoin JSON-RPC configuration</h2>
|
||||
<br>
|
||||
<label style="height: 25px; vertical-align: sub;">Auto-detect: </label>
|
||||
|
|
@ -140,6 +181,7 @@
|
|||
{% block scripts %}
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
setNodeTypeView("{{ node_view }}")
|
||||
var autodetect = document.getElementById("autodetect");
|
||||
if (autodetect != null) {
|
||||
function toggleAutoDetectMode() {
|
||||
|
|
@ -175,6 +217,20 @@
|
|||
toggleAutoDetectMode();
|
||||
}
|
||||
});
|
||||
|
||||
function setNodeTypeView(nodeType) {
|
||||
if (nodeType == "external") {
|
||||
document.getElementById("external_node_view_btn").classList.add('checked');
|
||||
document.getElementById("internal_node_view_btn").classList.remove('checked');
|
||||
document.getElementById("external_node_setup_view").classList.remove('hidden');
|
||||
document.getElementById("internal_node_setup_view").classList.add('hidden');
|
||||
} else {
|
||||
document.getElementById("external_node_view_btn").classList.remove('checked');
|
||||
document.getElementById("internal_node_view_btn").classList.add('checked');
|
||||
document.getElementById("external_node_setup_view").classList.add('hidden');
|
||||
document.getElementById("internal_node_setup_view").classList.remove('hidden');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
|
|
|||
|
|
@ -10,6 +10,24 @@
|
|||
<img style="width: 25px; margin-right: 7px; vertical-align: middle;" src="{{ url_for('static', filename='img/tor.svg') }}"/><span style="vertical-align: bottom; margin-right: 10px;">
|
||||
Tor configurations
|
||||
</h1>
|
||||
<div class="card center">
|
||||
<h2>Specer built-in Tor</h2><br>
|
||||
{% if torbrowser_installed %}
|
||||
<h2><b>Status: {% if torbrowser_running %}Running{% else %}Down{% endif %}</b></h2>
|
||||
<br>
|
||||
<div class="row">
|
||||
<button type="submit" class="btn" name="action" value="uninstalltor">Uninstall Tor</button>
|
||||
{% if torbrowser_running %}
|
||||
<button type="submit" class="btn" name="action" value="stoptor">Stop Tor</button>
|
||||
{% else %}
|
||||
<button type="submit" class="btn" name="action" value="starttor">Start Tor</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% else %}
|
||||
<button type="button" class="btn centered" id="setup-tor-button" onclick="setupTorDaemon()">Setup Tor!</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<br><br>
|
||||
Use Tor proxy running at:
|
||||
<input id="proxy-url" name="proxy_url" value="{{ proxy_url }}" type="url" placeholder="i.e. socks5://127.0.0.1:9050" />
|
||||
<br>
|
||||
|
|
@ -72,6 +90,83 @@
|
|||
{% endif %}
|
||||
</div>
|
||||
</form>
|
||||
<h1 id="stage-progress-details" class="hidden" style="margin: auto;"></h1>
|
||||
{% endblock %}
|
||||
{% block scripts %}
|
||||
<script>
|
||||
async function setupTorDaemon() {
|
||||
let url = "{{ url_for('setup_tor') }}"
|
||||
var formData = new FormData();
|
||||
formData.append('csrf_token', '{{ csrf_token() }}');
|
||||
try {
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'POST',
|
||||
body: formData
|
||||
}
|
||||
);
|
||||
if(response.status != 200){
|
||||
showError(await response.text());
|
||||
return;
|
||||
}
|
||||
const jsonResponse = await response.json();
|
||||
if ("success" in jsonResponse) {
|
||||
showNotification(jsonResponse.success);
|
||||
document.getElementById('setup-tor-button').classList.add('hidden');
|
||||
startProgressCheck();
|
||||
return;
|
||||
} if (jsonResponse.error == "tor is already installed") {
|
||||
showNotification('Tor daemon already installed.');
|
||||
return;
|
||||
}
|
||||
showError(jsonResponse.error, 4000);
|
||||
} catch(e) {
|
||||
showError("Failed to download Tor daemon...");
|
||||
showError(e);
|
||||
}
|
||||
}
|
||||
|
||||
function startProgressCheck() {
|
||||
async function fetchStageProgress() {
|
||||
try {
|
||||
let url="{{ url_for('get_tor_setup_status') }}"
|
||||
const response = await fetch(
|
||||
url,
|
||||
{
|
||||
method: 'GET'
|
||||
}
|
||||
);
|
||||
let result = await response.json();
|
||||
let stage = result.stage;
|
||||
if (result.error) {
|
||||
showError('Encountered an error:' + result.error);
|
||||
document.getElementById('stage-progress-details').classList.add('hidden');
|
||||
hidePageOverlay('stage-progress-details');
|
||||
return;
|
||||
}
|
||||
let progress = parseFloat(result.stage_progress);
|
||||
if (progress == -1) {
|
||||
showNotification('Finished successfully');
|
||||
document.getElementById('stage-progress-details').classList.add('hidden');
|
||||
hidePageOverlay('stage-progress-details');
|
||||
location.reload();
|
||||
return;
|
||||
}
|
||||
document.getElementById('stage-progress-details').innerHTML = `${stage}<br>`;
|
||||
if (progress > 0) {
|
||||
document.getElementById('stage-progress-details').innerHTML += `<br><br><b style="font-size:0.8em;">Progress: ${progress}%</b>`;
|
||||
}
|
||||
setTimeout(fetchStageProgress, 1000);
|
||||
} catch(e) {
|
||||
console.log('Caught error:', e);
|
||||
return { success: false, error: e };
|
||||
}
|
||||
}
|
||||
document.getElementById('stage-progress-details').classList.remove('hidden');
|
||||
showPageOverlay('stage-progress-details');
|
||||
document.getElementById('stage-progress-details').innerHTML = 'Stating up...';
|
||||
setTimeout(fetchStageProgress, 1000);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,8 @@
|
|||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
document.getElementById('export_wallet_file').href = 'data:text/json,' + encodeURIComponent('{{ wallet.account_map }}');
|
||||
let accountMap = encodeURIComponent(`{{ wallet.account_map|tojson }}`)
|
||||
document.getElementById('export_wallet_file').href = "data:text/json;charset=utf-8," + accountMap.substring(3, accountMap.length - 3);
|
||||
});
|
||||
document.addEventListener("DOMContentLoaded", function(){
|
||||
document.getElementById('electrum_export').href = 'data:text/plain,' + encodeURIComponent('{{ wallet.get_electrum_file()|tojson }}');
|
||||
|
|
|
|||
51
src/cryptoadvance/specter/tor_daemon.py
Normal file
51
src/cryptoadvance/specter/tor_daemon.py
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import logging
|
||||
import subprocess
|
||||
import platform
|
||||
import os
|
||||
import signal
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TorDaemonController:
|
||||
""" A class controlling the tor-daemon process directly on the machine """
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
tor_daemon_path,
|
||||
tor_config_path,
|
||||
):
|
||||
self.tor_daemon_path = tor_daemon_path
|
||||
self.tor_config_path = tor_config_path
|
||||
self.tor_daemon_proc = None
|
||||
|
||||
def start_tor_daemon(self, cleanup_at_exit=True):
|
||||
if self.is_running():
|
||||
return
|
||||
|
||||
self.tor_daemon_proc = subprocess.Popen(
|
||||
f'{"exec " if platform.system() != "Windows" else ""}"{self.tor_daemon_path}" --defaults-torrc {self.tor_config_path}',
|
||||
shell=True,
|
||||
)
|
||||
logger.debug(
|
||||
"Running tor-daemon process with pid {}".format(self.tor_daemon_proc.pid)
|
||||
)
|
||||
|
||||
def get_hashed_password(self, password):
|
||||
hashed_pw = subprocess.check_output(
|
||||
f'{"exec " if platform.system() != "Windows" else ""}"{self.tor_daemon_path}" --hash-password {password}',
|
||||
shell=True,
|
||||
).decode("utf8")
|
||||
|
||||
if not hashed_pw.startswith("16:"):
|
||||
return "16:" + hashed_pw.split("\n16:")[1]
|
||||
return hashed_pw
|
||||
|
||||
def is_running(self):
|
||||
return self.tor_daemon_proc and self.tor_daemon_proc.poll() is None
|
||||
|
||||
def stop_tor_daemon(self):
|
||||
if self.tor_daemon_proc:
|
||||
if platform.system() == "Windows":
|
||||
subprocess.run("Taskkill /IM tor.exe /F")
|
||||
self.tor_daemon_proc.terminate()
|
||||
199
src/cryptoadvance/specter/util/bitcoind_setup_tasks.py
Normal file
199
src/cryptoadvance/specter/util/bitcoind_setup_tasks.py
Normal file
|
|
@ -0,0 +1,199 @@
|
|||
import os, time, requests, secrets, platform, tarfile, zipfile, sys
|
||||
from ..bitcoind import BitcoindPlainController
|
||||
import pgpy
|
||||
from pathlib import Path
|
||||
from .sha256sum import sha256sum
|
||||
import logging
|
||||
from .file_download import download_file
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def setup_bitcoind_thread(specter=None, internal_bitcoind_version=""):
|
||||
try:
|
||||
BITCOIND_OS_SUFFIX = {
|
||||
"Windows": "win64.zip",
|
||||
"Linux": "x86_64-linux-gnu.tar.gz",
|
||||
"Darwin": "osx64.tar.gz",
|
||||
}
|
||||
bitcoind_url = f"https://bitcoincore.org/bin/bitcoin-core-{internal_bitcoind_version}/bitcoin-{internal_bitcoind_version}-{BITCOIND_OS_SUFFIX[platform.system()]}"
|
||||
packed_name = os.path.join(
|
||||
specter.data_folder,
|
||||
f"bitcoind-{BITCOIND_OS_SUFFIX[platform.system()]}",
|
||||
)
|
||||
download_file(
|
||||
specter,
|
||||
bitcoind_url,
|
||||
packed_name,
|
||||
"bitcoind",
|
||||
"Downloading Bitcoin Core release files...",
|
||||
)
|
||||
bitcoind_sha256sums_url = f"https://bitcoincore.org/bin/bitcoin-core-{internal_bitcoind_version}/SHA256SUMS.asc"
|
||||
bitcoind_sha256sums_file = os.path.join(
|
||||
specter.data_folder, "bitcoind-sha256sums.asc"
|
||||
)
|
||||
download_file(
|
||||
specter,
|
||||
bitcoind_sha256sums_url,
|
||||
bitcoind_sha256sums_file,
|
||||
"bitcoind",
|
||||
"Downloading Bitcoin Core signatures...",
|
||||
)
|
||||
specter.config["bitcoind_setup"]["stage"] = "Verifying signatures..."
|
||||
specter._save()
|
||||
with open(bitcoind_sha256sums_file, "r") as f:
|
||||
signed_sums = f.read()
|
||||
bitcoind_release_pgp_key, _ = pgpy.PGPKey.from_file(
|
||||
os.path.join(sys._MEIPASS, "static/bitcoin-release-pubkey.asc")
|
||||
if getattr(sys, "frozen", False)
|
||||
else Path(__file__).parent / "../static/bitcoin-release-pubkey.asc"
|
||||
)
|
||||
bitcoind_sha256sums_msg = pgpy.PGPMessage.from_file(
|
||||
bitcoind_sha256sums_file
|
||||
)
|
||||
if not bitcoind_release_pgp_key.verify(bitcoind_sha256sums_msg):
|
||||
raise Exception("Failed to verify Bitcoin Core PGP signature")
|
||||
bitcoind_hash = sha256sum(packed_name)
|
||||
if bitcoind_hash not in signed_sums:
|
||||
raise Exception("Failed to verify bitcoind hash is in SHA265SUMS.asc")
|
||||
|
||||
bitcoin_binaries_folder = os.path.join(specter.data_folder, "bitcoin-binaries")
|
||||
if packed_name.endswith("tar.gz"):
|
||||
with tarfile.open(packed_name, "r:gz") as so:
|
||||
so.extractall(specter.data_folder)
|
||||
else:
|
||||
with zipfile.ZipFile(packed_name, "r") as zip_ref:
|
||||
zip_ref.extractall(specter.data_folder)
|
||||
os.rename(
|
||||
os.path.join(specter.data_folder, f"bitcoin-{internal_bitcoind_version}"),
|
||||
bitcoin_binaries_folder,
|
||||
)
|
||||
os.remove(packed_name)
|
||||
specter.config["rpc"]["user"] = "bitcoin"
|
||||
specter.config["rpc"]["password"] = secrets.token_urlsafe(16)
|
||||
specter._save()
|
||||
if not os.path.exists(specter.config["rpc"]["datadir"]):
|
||||
os.makedirs(specter.config["rpc"]["datadir"])
|
||||
with open(
|
||||
os.path.join(specter.config["rpc"]["datadir"], "bitcoin.conf"),
|
||||
"w",
|
||||
) as file:
|
||||
file.write(f'\nrpcuser={specter.config["rpc"]["user"]}')
|
||||
file.write(f'\nrpcpassword={specter.config["rpc"]["password"]}')
|
||||
file.write(f"\nserver=1")
|
||||
file.write(f"\nlisten=1")
|
||||
file.write(f"\nproxy=127.0.0.1:9050")
|
||||
file.write(f"\nbind=127.0.0.1")
|
||||
file.write(f"\ntorcontrol=127.0.0.1:9051")
|
||||
file.write(f"\ntorpassword={specter.config['torrc_password']}")
|
||||
specter.config["bitcoind_internal_version"] = internal_bitcoind_version
|
||||
specter._save()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to install Bitcoin Core. Error: {e}")
|
||||
specter.config["bitcoind_setup"]["error"] = str(e)
|
||||
specter._save()
|
||||
finally:
|
||||
specter.config["bitcoind_setup"]["stage_progress"] = -1
|
||||
specter._save()
|
||||
|
||||
|
||||
def setup_bitcoind_directory_thread(specter=None, quicksync=True, pruned=True):
|
||||
try:
|
||||
if quicksync:
|
||||
prunednode_file = os.path.join(
|
||||
os.path.join(specter.data_folder, "snapshot-prunednode.zip")
|
||||
)
|
||||
prunednode_sha256sums_file = os.path.join(
|
||||
specter.data_folder, "prunednode-sha256sums.asc"
|
||||
)
|
||||
download_file(
|
||||
specter,
|
||||
"https://prunednode.today/latest.zip",
|
||||
prunednode_file,
|
||||
"bitcoind",
|
||||
"Downloading QuickSync files...",
|
||||
)
|
||||
download_file(
|
||||
specter,
|
||||
"https://prunednode.today/latest.signed.txt",
|
||||
prunednode_sha256sums_file,
|
||||
"bitcoind",
|
||||
"Downloading Quicksync signature...",
|
||||
)
|
||||
specter.config["bitcoind_setup"]["stage"] = "Verifying signatures..."
|
||||
specter._save()
|
||||
with open(prunednode_sha256sums_file, "r") as f:
|
||||
signed_sums = f.read()
|
||||
prunednode_release_pgp_key, _ = pgpy.PGPKey.from_file(
|
||||
os.path.join(
|
||||
sys._MEIPASS, "static/pruned-node-today-release-pubkey.asc"
|
||||
)
|
||||
if getattr(sys, "frozen", False)
|
||||
else Path(__file__).parent
|
||||
/ "../static/pruned-node-today-release-pubkey.asc"
|
||||
)
|
||||
prunednode_sha256sums_msg = pgpy.PGPMessage.from_file(
|
||||
prunednode_sha256sums_file
|
||||
)
|
||||
if not prunednode_release_pgp_key.verify(prunednode_sha256sums_msg):
|
||||
raise Exception("Failed to verify prunednode.today PGP signature")
|
||||
prunednode_hash = sha256sum(prunednode_file)
|
||||
if prunednode_hash not in signed_sums:
|
||||
raise Exception(
|
||||
"Failed to verify prunednode.today hash is in SHA265SUMS.asc"
|
||||
)
|
||||
with zipfile.ZipFile(prunednode_file, "r") as zip_ref:
|
||||
zip_ref.extractall(
|
||||
os.path.expanduser(specter.config["rpc"]["datadir"])
|
||||
)
|
||||
os.remove(prunednode_file)
|
||||
with open(
|
||||
os.path.join(specter.config["rpc"]["datadir"], "bitcoin.conf"),
|
||||
"a",
|
||||
) as file:
|
||||
file.write(f'\nrpcuser={specter.config["rpc"]["user"]}')
|
||||
file.write(f'\nrpcpassword={specter.config["rpc"]["password"]}')
|
||||
else:
|
||||
with open(
|
||||
os.path.join(specter.config["rpc"]["datadir"], "bitcoin.conf"),
|
||||
"a",
|
||||
) as file:
|
||||
if pruned:
|
||||
file.write(f"\nprune=1000")
|
||||
else:
|
||||
file.write(f"\nblockfilterindex=1")
|
||||
|
||||
specter.config["bitcoind_setup"]["stage"] = "Starting up Bitcoin Core..."
|
||||
specter._save()
|
||||
specter.bitcoind = BitcoindPlainController(
|
||||
bitcoind_path=specter.bitcoind_path,
|
||||
rpcport=8332,
|
||||
network="mainnet",
|
||||
rpcuser=specter.config["rpc"]["user"],
|
||||
rpcpassword=specter.config["rpc"]["password"],
|
||||
)
|
||||
specter.bitcoind.start_bitcoind(
|
||||
datadir=os.path.expanduser(specter.config["rpc"]["datadir"])
|
||||
)
|
||||
specter.set_bitcoind_pid(specter.bitcoind.bitcoind_proc.pid)
|
||||
specter.update_use_external_node(False)
|
||||
time.sleep(15)
|
||||
success = specter.update_rpc(
|
||||
port=8332,
|
||||
autodetect=True,
|
||||
user=specter.config["rpc"]["user"],
|
||||
password=specter.config["rpc"]["password"],
|
||||
)
|
||||
if not success:
|
||||
specter.config["bitcoind_setup"][
|
||||
"stage"
|
||||
] = "Failed to start Bitcoin Core..."
|
||||
specter._save()
|
||||
specter.check()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to setup Bitcoin Core. Error: {e}")
|
||||
specter.config["bitcoind_setup"]["error"] = str(e)
|
||||
specter._save()
|
||||
finally:
|
||||
specter.config["bitcoind_setup"]["stage_progress"] = -1
|
||||
specter._save()
|
||||
15
src/cryptoadvance/specter/util/error_handling.py
Normal file
15
src/cryptoadvance/specter/util/error_handling.py
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def handle_exception(exception, user=None):
|
||||
""" prints the exception and most important the stacktrace """
|
||||
logger.error("Unexpected error:")
|
||||
logger.error(
|
||||
"----START-TRACEBACK-----------------------------------------------------------------"
|
||||
)
|
||||
logger.exception(exception) # the exception instance
|
||||
logger.error(
|
||||
"----END---TRACEBACK-----------------------------------------------------------------"
|
||||
)
|
||||
23
src/cryptoadvance/specter/util/file_download.py
Normal file
23
src/cryptoadvance/specter/util/file_download.py
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import os
|
||||
|
||||
|
||||
def download_file(specter, url, file_path, setup_status_name, setup_status_stage):
|
||||
response = specter.requests_session().get(url, stream=True)
|
||||
with open(file_path, "wb") as f:
|
||||
total_length = float(response.headers["content-length"])
|
||||
downloaded = 0.0
|
||||
old_progress = 0
|
||||
specter.config[f"{setup_status_name}_setup"]["stage"] = setup_status_stage
|
||||
specter._save()
|
||||
for chunk in response.iter_content(chunk_size=4096):
|
||||
downloaded += len(chunk)
|
||||
f.write(chunk)
|
||||
new_progress = int((downloaded / total_length) * 10000) / 100
|
||||
if new_progress > old_progress:
|
||||
if new_progress > 100:
|
||||
new_progress = 100
|
||||
old_progress = new_progress
|
||||
specter.config[f"{setup_status_name}_setup"][
|
||||
"stage_progress"
|
||||
] = new_progress
|
||||
specter._save()
|
||||
55
src/cryptoadvance/specter/util/rpcauth.py
Normal file
55
src/cryptoadvance/specter/util/rpcauth.py
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2015-2018 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
from argparse import ArgumentParser
|
||||
from base64 import urlsafe_b64encode
|
||||
from binascii import hexlify
|
||||
from getpass import getpass
|
||||
from os import urandom
|
||||
|
||||
import hmac
|
||||
|
||||
|
||||
def generate_salt(size):
|
||||
"""Create size byte hex salt"""
|
||||
return hexlify(urandom(size)).decode()
|
||||
|
||||
|
||||
def generate_password():
|
||||
"""Create 32 byte b64 password"""
|
||||
return urlsafe_b64encode(urandom(32)).decode("utf-8")
|
||||
|
||||
|
||||
def password_to_hmac(salt, password):
|
||||
m = hmac.new(bytearray(salt, "utf-8"), bytearray(password, "utf-8"), "SHA256")
|
||||
return m.hexdigest()
|
||||
|
||||
|
||||
def main():
|
||||
parser = ArgumentParser(description="Create login credentials for a JSON-RPC user")
|
||||
parser.add_argument("username", help="the username for authentication")
|
||||
parser.add_argument(
|
||||
"password",
|
||||
help='leave empty to generate a random password or specify "-" to prompt for password',
|
||||
nargs="?",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
if not args.password:
|
||||
args.password = generate_password()
|
||||
elif args.password == "-":
|
||||
args.password = getpass()
|
||||
|
||||
# Create 16 byte hex salt
|
||||
salt = generate_salt(16)
|
||||
password_hmac = password_to_hmac(salt, args.password)
|
||||
|
||||
print("String to be appended to bitcoin.conf:")
|
||||
print("rpcauth={0}:{1}${2}".format(args.username, salt, password_hmac))
|
||||
print("Your password:\n{0}".format(args.password))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
11
src/cryptoadvance/specter/util/sha256sum.py
Normal file
11
src/cryptoadvance/specter/util/sha256sum.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import hashlib
|
||||
|
||||
|
||||
def sha256sum(filename):
|
||||
h = hashlib.sha256()
|
||||
b = bytearray(128 * 1024)
|
||||
mv = memoryview(b)
|
||||
with open(filename, "rb", buffering=0) as f:
|
||||
for n in iter(lambda: f.readinto(mv), 0):
|
||||
h.update(mv[:n])
|
||||
return h.hexdigest()
|
||||
|
|
@ -1,16 +1,26 @@
|
|||
import os
|
||||
import os, platform
|
||||
from stem.control import Controller
|
||||
|
||||
|
||||
def get_tor_daemon_suffix():
|
||||
if platform.system() == "Darwin":
|
||||
return ".real"
|
||||
elif platform.system() == "Windows":
|
||||
return ".exe"
|
||||
return ""
|
||||
|
||||
|
||||
def start_hidden_service(app):
|
||||
app.controller.reconnect()
|
||||
app.specter.tor_controller.authenticate(
|
||||
password=app.specter.config.get("torrc_password", "")
|
||||
)
|
||||
key_path = os.path.abspath(
|
||||
os.path.join(app.specter.data_folder, ".tor_service_key")
|
||||
)
|
||||
app.tor_service_id = None
|
||||
|
||||
if not os.path.exists(key_path):
|
||||
service = app.controller.create_ephemeral_hidden_service(
|
||||
service = app.specter.tor_controller.create_ephemeral_hidden_service(
|
||||
{app.tor_port: app.port}, await_publication=True
|
||||
)
|
||||
app.tor_service_id = service.service_id
|
||||
|
|
@ -25,7 +35,7 @@ def start_hidden_service(app):
|
|||
with open(key_path) as key_file:
|
||||
key_type, key_content = key_file.read().split(":", 1)
|
||||
|
||||
service = app.controller.create_ephemeral_hidden_service(
|
||||
service = app.specter.tor_controller.create_ephemeral_hidden_service(
|
||||
{app.tor_port: app.port},
|
||||
key_type=key_type,
|
||||
key_content=key_content,
|
||||
|
|
@ -62,13 +72,15 @@ def start_hidden_service(app):
|
|||
|
||||
def stop_hidden_services(app):
|
||||
try:
|
||||
app.controller.reconnect()
|
||||
hidden_services = app.controller.list_ephemeral_hidden_services()
|
||||
app.specter.tor_controller.authenticate(
|
||||
password=app.specter.config.get("torrc_password", "")
|
||||
)
|
||||
hidden_services = app.specter.tor_controller.list_ephemeral_hidden_services()
|
||||
print(" * Shutting down our hidden service")
|
||||
for tor_service_id in hidden_services:
|
||||
app.controller.remove_ephemeral_hidden_service(tor_service_id)
|
||||
app.specter.tor_controller.remove_ephemeral_hidden_service(tor_service_id)
|
||||
# Sanity
|
||||
if len(app.controller.list_ephemeral_hidden_services()) != 0:
|
||||
if len(app.specter.tor_controller.list_ephemeral_hidden_services()) != 0:
|
||||
print(" * Failed to shut down our hidden services...")
|
||||
else:
|
||||
print(" * Hidden services were shut down successfully")
|
||||
|
|
|
|||
132
src/cryptoadvance/specter/util/tor_setup_tasks.py
Normal file
132
src/cryptoadvance/specter/util/tor_setup_tasks.py
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
import os, time, requests, secrets, platform, tarfile, zipfile, sys, subprocess, shutil, stat, zipfile, logging
|
||||
import pgpy
|
||||
from pathlib import Path
|
||||
from .sha256sum import sha256sum
|
||||
from .file_download import download_file
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def copytree(src, dst, symlinks=False, ignore=None):
|
||||
for item in os.listdir(src):
|
||||
s = os.path.join(src, item)
|
||||
d = os.path.join(dst, item)
|
||||
if os.path.isdir(s):
|
||||
shutil.copytree(s, d, symlinks, ignore)
|
||||
else:
|
||||
shutil.copy2(s, d)
|
||||
|
||||
|
||||
def setup_tor_thread(specter=None):
|
||||
try:
|
||||
specter.config["torbrowser_setup"]["stage"] = "Starting Tor setup process..."
|
||||
specter._save()
|
||||
TOR_OS_SUFFIX = {
|
||||
"Windows": "tor-win64-0.4.5.6.zip",
|
||||
"Linux": "tor-browser-linux64-10.0.12_en-US.tar.xz",
|
||||
"Darwin": "TorBrowser-10.0.12-osx64_en-US.dmg",
|
||||
}
|
||||
|
||||
torbrowser_url = f"https://www.torproject.org/dist/torbrowser/10.0.12/{TOR_OS_SUFFIX[platform.system()]}"
|
||||
packed_name = os.path.join(
|
||||
specter.data_folder,
|
||||
TOR_OS_SUFFIX[platform.system()],
|
||||
)
|
||||
torbrowser_signed_url = f"https://www.torproject.org/dist/torbrowser/10.0.12/{TOR_OS_SUFFIX[platform.system()]}.asc"
|
||||
torbrowser_signed_file = os.path.join(
|
||||
specter.data_folder, f"{TOR_OS_SUFFIX[platform.system()]}.asc"
|
||||
)
|
||||
download_file(
|
||||
specter,
|
||||
torbrowser_url,
|
||||
packed_name,
|
||||
"torbrowser",
|
||||
"Downloading Tor release files...",
|
||||
)
|
||||
download_file(
|
||||
specter,
|
||||
torbrowser_signed_url,
|
||||
torbrowser_signed_file,
|
||||
"torbrowser",
|
||||
"Downloading Tor signatures...",
|
||||
)
|
||||
specter.config["torbrowser_setup"]["stage"] = "Verifying signatures..."
|
||||
specter._save()
|
||||
torbrowser_release_pgp_key, _ = pgpy.PGPKey.from_file(
|
||||
os.path.join(sys._MEIPASS, "static/torbrowser-release-pubkey.asc")
|
||||
if getattr(sys, "frozen", False)
|
||||
else Path(__file__).parent / "../static/torbrowser-release-pubkey.asc"
|
||||
)
|
||||
torbrowser_file_sig = pgpy.PGPSignature.from_file(torbrowser_signed_file)
|
||||
with open(packed_name, "rb") as binary_file:
|
||||
signed_data = binary_file.read()
|
||||
if not torbrowser_release_pgp_key.verify(signed_data, torbrowser_file_sig):
|
||||
raise Exception("Failed to verify Tor PGP signature")
|
||||
|
||||
os.makedirs(os.path.join(specter.data_folder, "tor-binaries"), exist_ok=True)
|
||||
if packed_name.endswith(".dmg"):
|
||||
result = subprocess.run(
|
||||
[f'hdiutil attach "{packed_name}"'], shell=True, capture_output=True
|
||||
)
|
||||
if result.stderr == b"hdiutil: attach failed - no mountable file systems\n":
|
||||
os.remove(packed_name)
|
||||
return
|
||||
torbrowser_source_path = os.path.join(
|
||||
"/Volumes", "Tor Browser", "Tor Browser.app", "Contents", "MacOS", "Tor"
|
||||
)
|
||||
copytree(
|
||||
src=torbrowser_source_path,
|
||||
dst=os.path.join(specter.data_folder, "tor-binaries"),
|
||||
)
|
||||
disk_image_path = "/Volumes/Tor\ Browser"
|
||||
subprocess.run([f"hdiutil detach {disk_image_path}"], shell=True)
|
||||
elif packed_name.endswith(".zip"):
|
||||
with zipfile.ZipFile(packed_name) as zip_file:
|
||||
for file in zip_file.filelist:
|
||||
if file.filename.endswith("dll") or file.filename.endswith("exe"):
|
||||
destination_exe = os.path.join(
|
||||
os.path.join(specter.data_folder, "tor-binaries"),
|
||||
file.filename.split("/")[-1],
|
||||
)
|
||||
with zip_file.open(file.filename) as zf, open(
|
||||
destination_exe, "wb"
|
||||
) as f:
|
||||
shutil.copyfileobj(zf, f)
|
||||
elif packed_name.endswith(".tar.xz"):
|
||||
with tarfile.open(packed_name) as tar:
|
||||
tor_files = [
|
||||
"libcrypto.so.1.1",
|
||||
"libevent-2.1.so.7",
|
||||
"libssl.so.1.1",
|
||||
"tor",
|
||||
]
|
||||
for tor_file in tor_files:
|
||||
file_name = "tor-browser_en-US/Browser/TorBrowser/Tor/" + tor_file
|
||||
destination_file = os.path.join(
|
||||
os.path.join(specter.data_folder, "tor-binaries"), tor_file
|
||||
)
|
||||
extracted_file = tar.extractfile(file_name)
|
||||
with open(destination_file, "wb") as f:
|
||||
shutil.copyfileobj(extracted_file, f)
|
||||
if tor_file == "tor":
|
||||
st = os.stat(destination_file)
|
||||
os.chmod(destination_file, st.st_mode | stat.S_IEXEC)
|
||||
os.remove(packed_name)
|
||||
if "torrc_password" not in specter.config:
|
||||
specter.config["torrc_password"] = secrets.token_urlsafe(16)
|
||||
specter._save()
|
||||
with open(os.path.join(specter.data_folder, "torrc"), "w") as file:
|
||||
file.write("ControlPort 9051")
|
||||
file.write(
|
||||
f"\nHashedControlPassword {specter.tor_daemon.get_hashed_password(specter.config['torrc_password'])}"
|
||||
)
|
||||
|
||||
specter.tor_daemon.start_tor_daemon()
|
||||
specter.update_tor_controller()
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to install Tor. Error: {e}")
|
||||
specter.config["torbrowser_setup"]["error"] = str(e)
|
||||
specter._save()
|
||||
finally:
|
||||
specter.config["torbrowser_setup"]["stage_progress"] = -1
|
||||
specter._save()
|
||||
|
|
@ -10,9 +10,9 @@ import time
|
|||
import pytest
|
||||
import docker
|
||||
from cryptoadvance.specter.bitcoind import (
|
||||
BitcoindDockerController,
|
||||
BitcoindPlainController,
|
||||
)
|
||||
from cryptoadvance.specter.bitcoind_docker import BitcoindDockerController
|
||||
from cryptoadvance.specter.device_manager import DeviceManager
|
||||
from cryptoadvance.specter.specter import Specter
|
||||
from cryptoadvance.specter.server import create_app, init_app
|
||||
|
|
@ -48,6 +48,8 @@ def instantiate_bitcoind_controller(docker, request, rpcport=18543, extra_args=[
|
|||
# logging.getLogger().setLevel(logging.DEBUG)
|
||||
requested_version = request.config.getoption("--bitcoind-version")
|
||||
if docker:
|
||||
from cryptoadvance.specter.bitcoind_docker import BitcoindDockerController
|
||||
|
||||
bitcoind_controller = BitcoindDockerController(
|
||||
rpcport=rpcport, docker_tag=requested_version
|
||||
)
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import logging
|
|||
import os
|
||||
from cryptoadvance.specter.util.shell import which
|
||||
from cryptoadvance.specter.bitcoind import BitcoindPlainController
|
||||
from cryptoadvance.specter.bitcoind import BitcoindDockerController
|
||||
from cryptoadvance.specter.bitcoind import fetch_wallet_addresses_for_mining
|
||||
from cryptoadvance.specter.bitcoind_docker import BitcoindDockerController
|
||||
|
||||
|
||||
def test_bitcoinddocker_running(caplog, docker, request):
|
||||
|
|
@ -13,6 +13,8 @@ def test_bitcoinddocker_running(caplog, docker, request):
|
|||
caplog.set_level(logging.DEBUG, logger="cryptoadvance.specter")
|
||||
requested_version = request.config.getoption("--bitcoind-version")
|
||||
if docker:
|
||||
from cryptoadvance.specter.bitcoind_docker import BitcoindDockerController
|
||||
|
||||
my_bitcoind = BitcoindDockerController(
|
||||
rpcport=18999, docker_tag=requested_version
|
||||
) # completly different port to not interfere
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue