mirror of
https://github.com/BlueWallet/BlueWallet.git
synced 2025-01-18 21:35:21 +01:00
ADD: TOR support
This commit is contained in:
parent
2e2709816d
commit
1bdd37d0fb
@ -15,6 +15,7 @@ import EncryptStorage from './screen/settings/encryptStorage';
|
||||
import PlausibleDeniability from './screen/plausibledeniability';
|
||||
import LightningSettings from './screen/settings/lightningSettings';
|
||||
import ElectrumSettings from './screen/settings/electrumSettings';
|
||||
import TorSettings from './screen/settings/torSettings';
|
||||
import Tools from './screen/settings/tools';
|
||||
import GeneralSettings from './screen/settings/GeneralSettings';
|
||||
import NetworkSettings from './screen/settings/NetworkSettings';
|
||||
@ -153,6 +154,7 @@ const WalletsRoot = () => {
|
||||
/>
|
||||
<WalletsStack.Screen name="LightningSettings" component={LightningSettings} options={LightningSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="ElectrumSettings" component={ElectrumSettings} options={ElectrumSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="TorSettings" component={TorSettings} options={TorSettings.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="SettingsPrivacy" component={SettingsPrivacy} options={SettingsPrivacy.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="Tools" component={Tools} options={Tools.navigationOptions(theme)} />
|
||||
<WalletsStack.Screen name="LNDViewInvoice" component={LNDViewInvoice} options={LNDViewInvoice.navigationOptions(theme)} />
|
||||
|
18
__mocks__/react-native-tor.js
vendored
Normal file
18
__mocks__/react-native-tor.js
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/* global jest */
|
||||
|
||||
export const startIfNotStarted = jest.fn(async (key, value, callback) => {
|
||||
return 666;
|
||||
});
|
||||
|
||||
|
||||
export const get = jest.fn();
|
||||
export const post = jest.fn();
|
||||
export const deleteMock = jest.fn();
|
||||
export const stopIfRunning = jest.fn();
|
||||
export const getDaemonStatus = jest.fn();
|
||||
|
||||
const mock = jest.fn().mockImplementation(() => {
|
||||
return { startIfNotStarted, get, post, delete: deleteMock, stopIfRunning, getDaemonStatus };
|
||||
});
|
||||
|
||||
export default mock;
|
@ -177,6 +177,7 @@ android {
|
||||
|
||||
dependencies {
|
||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||
implementation files("../../node_modules/react-native-tor/android/libs/sifir_android.aar")
|
||||
//noinspection GradleDynamicVersion
|
||||
|
||||
androidTestImplementation('com.wix:detox:+') {
|
||||
|
4
android/app/proguard-rules.pro
vendored
4
android/app/proguard-rules.pro
vendored
@ -8,3 +8,7 @@
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
-keep class com.sifir.** { *;}
|
||||
-keep interface com.sifir.** { *;}
|
||||
-keep enum com.sifir.** { *;}
|
@ -14,6 +14,7 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:allowBackup="false"
|
||||
android:extractNativeLibs="true"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:supportsRtl="true"
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
minSdkVersion = 21
|
||||
minSdkVersion = 26
|
||||
supportLibVersion = "28.0.0"
|
||||
buildToolsVersion = "29.0.2"
|
||||
compileSdkVersion = 29
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* global alert */
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { Platform, Alert } from 'react-native';
|
||||
import { Alert } from 'react-native';
|
||||
import { AppStorage, LegacyWallet, SegwitBech32Wallet, SegwitP2SHWallet } from '../class';
|
||||
import DefaultPreference from 'react-native-default-preference';
|
||||
import RNWidgetCenter from 'react-native-widget-center';
|
||||
@ -9,6 +9,7 @@ const bitcoin = require('bitcoinjs-lib');
|
||||
const ElectrumClient = require('electrum-client');
|
||||
const reverse = require('buffer-reverse');
|
||||
const BigNumber = require('bignumber.js');
|
||||
const torrific = require('../blue_modules/torrific');
|
||||
|
||||
const storageKey = 'ELECTRUM_PEERS';
|
||||
const defaultPeer = { host: 'electrum1.bluewallet.io', ssl: '443' };
|
||||
@ -48,11 +49,19 @@ async function connectMain() {
|
||||
usingPeer = savedPeer;
|
||||
}
|
||||
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
try {
|
||||
await DefaultPreference.setName('group.io.bluewallet.bluewallet');
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, usingPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, usingPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, usingPeer.ssl);
|
||||
if (usingPeer.host.endsWith('onion')) {
|
||||
const randomPeer = await getRandomHardcodedPeer();
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, randomPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, randomPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, randomPeer.ssl);
|
||||
} else {
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_HOST, usingPeer.host);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_TCP_PORT, usingPeer.tcp);
|
||||
await DefaultPreference.set(AppStorage.ELECTRUM_SSL_PORT, usingPeer.ssl);
|
||||
}
|
||||
|
||||
RNWidgetCenter.reloadAllTimelines();
|
||||
} catch (e) {
|
||||
// Must be running on Android
|
||||
@ -61,19 +70,26 @@ async function connectMain() {
|
||||
|
||||
try {
|
||||
console.log('begin connection:', JSON.stringify(usingPeer));
|
||||
mainClient = new ElectrumClient(usingPeer.ssl || usingPeer.tcp, usingPeer.host, usingPeer.ssl ? 'tls' : 'tcp');
|
||||
mainClient = new ElectrumClient(
|
||||
usingPeer.host.endsWith('.onion') ? torrific : global.net,
|
||||
global.tls,
|
||||
usingPeer.ssl || usingPeer.tcp,
|
||||
usingPeer.host,
|
||||
usingPeer.ssl ? 'tls' : 'tcp',
|
||||
);
|
||||
mainClient.onError = function (e) {
|
||||
if (Platform.OS === 'android' && mainConnected) {
|
||||
// android sockets are buggy and dont always issue CLOSE event, which actually makes the persistence code to reconnect.
|
||||
// so lets do it manually, but only if we were previously connected (mainConnected), otherwise theres other
|
||||
console.log('electrum mainClient.onError():', e.message);
|
||||
if (mainConnected) {
|
||||
// most likely got a timeout from electrum ping. lets reconnect
|
||||
// but only if we were previously connected (mainConnected), otherwise theres other
|
||||
// code which does connection retries
|
||||
mainClient.close();
|
||||
mainConnected = false;
|
||||
setTimeout(connectMain, 500);
|
||||
// dropping `mainConnected` flag ensures there wont be reconnection race condition if several
|
||||
// errors triggered
|
||||
console.log('reconnecting after socket error');
|
||||
return;
|
||||
setTimeout(connectMain, usingPeer.host.endsWith('.onion') ? 4000 : 500);
|
||||
}
|
||||
mainConnected = false;
|
||||
};
|
||||
const ver = await mainClient.initElectrum({ client: 'bluewallet', version: '1.4' });
|
||||
if (ver && ver[0]) {
|
||||
@ -104,6 +120,7 @@ async function connectMain() {
|
||||
if (connectionAttempt >= 5) {
|
||||
presentNetworkErrorAlert(usingPeer);
|
||||
} else {
|
||||
console.log('reconnection attempt #', connectionAttempt);
|
||||
setTimeout(connectMain, 500);
|
||||
}
|
||||
}
|
||||
@ -249,8 +266,8 @@ module.exports.getConfig = async function () {
|
||||
return {
|
||||
host: mainClient.host,
|
||||
port: mainClient.port,
|
||||
status: mainClient.status ? 1 : 0,
|
||||
serverName,
|
||||
connected: mainClient.timeLastCall !== 0 && mainClient.status,
|
||||
};
|
||||
};
|
||||
|
||||
@ -522,18 +539,19 @@ module.exports.waitTillConnected = async function () {
|
||||
waitTillConnectedInterval = setInterval(() => {
|
||||
if (mainConnected) {
|
||||
clearInterval(waitTillConnectedInterval);
|
||||
resolve(true);
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
if (wasConnectedAtLeastOnce && mainClient.status === 1) {
|
||||
clearInterval(waitTillConnectedInterval);
|
||||
mainConnected = true;
|
||||
resolve(true);
|
||||
return resolve(true);
|
||||
}
|
||||
|
||||
if (retriesCounter++ >= 30) {
|
||||
if (wasConnectedAtLeastOnce && retriesCounter++ >= 30) {
|
||||
// `wasConnectedAtLeastOnce` needed otherwise theres gona be a race condition with the code that connects
|
||||
// electrum during app startup
|
||||
clearInterval(waitTillConnectedInterval);
|
||||
connectionAttempt = 0;
|
||||
presentNetworkErrorAlert();
|
||||
reject(new Error('Waiting for Electrum connection timeout'));
|
||||
}
|
||||
@ -600,7 +618,12 @@ module.exports.calcEstimateFeeFromFeeHistorgam = function (numberOfBlocks, feeHi
|
||||
};
|
||||
|
||||
module.exports.estimateFees = async function () {
|
||||
const histogram = await mainClient.mempool_getFeeHistogram();
|
||||
let histogram;
|
||||
try {
|
||||
histogram = await Promise.race([mainClient.mempool_getFeeHistogram(), new Promise(resolve => setTimeout(resolve, 5000))]);
|
||||
} catch (_) {}
|
||||
|
||||
if (!histogram) throw new Error('timeout while getting mempool_getFeeHistogram');
|
||||
|
||||
// fetching what electrum (which uses bitcoin core) thinks about fees:
|
||||
const _fast = await module.exports.estimateFee(1);
|
||||
@ -685,13 +708,20 @@ module.exports.calculateBlockTime = function (height) {
|
||||
* @returns {Promise<boolean>} Whether provided host:port is a valid electrum server
|
||||
*/
|
||||
module.exports.testConnection = async function (host, tcpPort, sslPort) {
|
||||
const client = new ElectrumClient(sslPort || tcpPort, host, sslPort ? 'tls' : 'tcp');
|
||||
const client = new ElectrumClient(
|
||||
host.endsWith('.onion') ? torrific : global.net,
|
||||
global.tls,
|
||||
sslPort || tcpPort,
|
||||
host,
|
||||
sslPort ? 'tls' : 'tcp',
|
||||
);
|
||||
|
||||
client.onError = () => {}; // mute
|
||||
let timeoutId = false;
|
||||
try {
|
||||
const rez = await Promise.race([
|
||||
new Promise(resolve => {
|
||||
timeoutId = setTimeout(() => resolve('timeout'), 3000);
|
||||
timeoutId = setTimeout(() => resolve('timeout'), host.endsWith('.onion') ? 21000 : 5000);
|
||||
}),
|
||||
client.connect(),
|
||||
]);
|
||||
@ -714,6 +744,7 @@ module.exports.forceDisconnect = () => {
|
||||
};
|
||||
|
||||
module.exports.hardcodedPeers = hardcodedPeers;
|
||||
module.exports.getRandomHardcodedPeer = getRandomHardcodedPeer;
|
||||
|
||||
const splitIntoChunks = function (arr, chunkSize) {
|
||||
const groups = [];
|
||||
|
@ -61,7 +61,6 @@ async function updateExchangeRate() {
|
||||
try {
|
||||
rate = await getFiatRate(preferredFiatCurrency.endPointKey);
|
||||
} catch (Err) {
|
||||
console.warn(Err);
|
||||
const lastSavedExchangeRate = JSON.parse(await AsyncStorage.getItem(AppStorage.EXCHANGE_RATES));
|
||||
exchangeRates['BTC_' + preferredFiatCurrency.endPointKey] = lastSavedExchangeRate['BTC_' + preferredFiatCurrency.endPointKey] * 1;
|
||||
return;
|
||||
|
287
blue_modules/torrific.js
Normal file
287
blue_modules/torrific.js
Normal file
@ -0,0 +1,287 @@
|
||||
import Tor from 'react-native-tor';
|
||||
const tor = Tor();
|
||||
|
||||
/**
|
||||
* TOR wrapper mimicking Frisbee interface
|
||||
*/
|
||||
class Torsbee {
|
||||
baseURI = '';
|
||||
|
||||
static _testConn;
|
||||
static _resolveReference;
|
||||
static _rejectReference;
|
||||
|
||||
constructor(opts) {
|
||||
opts = opts || {};
|
||||
this.baseURI = opts.baseURI || this.baseURI;
|
||||
}
|
||||
|
||||
async get(path, options) {
|
||||
console.log('TOR: starting...');
|
||||
const socksProxy = await tor.startIfNotStarted();
|
||||
console.log('TOR: started', await tor.getDaemonStatus(), 'on local port', socksProxy);
|
||||
if (path.startsWith('/') && this.baseURI.endsWith('/')) {
|
||||
// oy vey, duplicate slashes
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
const response = {};
|
||||
try {
|
||||
const uri = this.baseURI + path;
|
||||
console.log('TOR: requesting', uri);
|
||||
const torResponse = await tor.get(uri, options?.headers || {}, true);
|
||||
response.originalResponse = torResponse;
|
||||
|
||||
if (options?.headers['Content-Type'] === 'application/json' && torResponse.json) {
|
||||
response.body = torResponse.json;
|
||||
} else {
|
||||
response.body = Buffer.from(torResponse.b64Data, 'base64').toString();
|
||||
}
|
||||
} catch (error) {
|
||||
response.err = error;
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
async post(path, options) {
|
||||
console.log('TOR: starting...');
|
||||
const socksProxy = await tor.startIfNotStarted();
|
||||
console.log('TOR: started', await tor.getDaemonStatus(), 'on local port', socksProxy);
|
||||
if (path.startsWith('/') && this.baseURI.endsWith('/')) {
|
||||
// oy vey, duplicate slashes
|
||||
path = path.substr(1);
|
||||
}
|
||||
|
||||
const uri = this.baseURI + path;
|
||||
console.log('TOR: posting to', uri);
|
||||
|
||||
const response = {};
|
||||
try {
|
||||
const torResponse = await tor.post(uri, JSON.stringify(options?.body || {}), options?.headers || {}, true);
|
||||
response.originalResponse = torResponse;
|
||||
|
||||
if (options?.headers['Content-Type'] === 'application/json' && torResponse.json) {
|
||||
response.body = torResponse.json;
|
||||
} else {
|
||||
response.body = Buffer.from(torResponse.b64Data, 'base64').toString();
|
||||
}
|
||||
} catch (error) {
|
||||
response.err = error;
|
||||
console.warn(error);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
testSocket() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.constructor._resolveReference = resolve;
|
||||
this.constructor._rejectReference = reject;
|
||||
(async () => {
|
||||
console.log('testSocket...');
|
||||
try {
|
||||
if (!this.constructor._testConn) {
|
||||
// no test conenctino exists, creating it...
|
||||
await tor.startIfNotStarted();
|
||||
const target = 'v7gtzf7nua6hdmb2wtqaqioqmesdb4xrlly4zwr7bvayxv2bpg665pqd.onion:50001';
|
||||
this.constructor._testConn = await tor.createTcpConnection({ target }, (data, err) => {
|
||||
if (err) {
|
||||
return this.constructor._rejectReference(new Error(err));
|
||||
}
|
||||
const json = JSON.parse(data);
|
||||
if (!json || typeof json.result === 'undefined')
|
||||
return this.constructor._rejectReference(new Error('Unexpected response from TOR socket: ' + JSON.stringify(json)));
|
||||
|
||||
// conn.close();
|
||||
// instead of closing connect, we will actualy re-cyce existing test connection as we
|
||||
// saved it into `this.constructor.testConn`
|
||||
this.constructor._resolveReference();
|
||||
});
|
||||
|
||||
await this.constructor._testConn.write(
|
||||
`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`,
|
||||
);
|
||||
} else {
|
||||
// test connectino exists, so we are reusing it
|
||||
await this.constructor._testConn.write(
|
||||
`{ "id": 1, "method": "blockchain.scripthash.get_balance", "params": ["716decbe1660861c3d93906cb1d98ee68b154fd4d23aed9783859c1271b52a9c"] }\n`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
this.constructor._rejectReference(error);
|
||||
}
|
||||
})();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper for react-native-tor mimicking Socket class from NET package
|
||||
*/
|
||||
class TorSocket {
|
||||
constructor() {
|
||||
this._socket = false;
|
||||
this._listeners = {};
|
||||
}
|
||||
|
||||
setTimeout() {}
|
||||
|
||||
setEncoding() {}
|
||||
|
||||
setKeepAlive() {}
|
||||
|
||||
setNoDelay() {}
|
||||
|
||||
on(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
this._listeners[event].push(listener);
|
||||
}
|
||||
|
||||
removeListener(event, listener) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
const newListeners = [];
|
||||
|
||||
let found = false;
|
||||
for (const savedListener of this._listeners[event]) {
|
||||
// eslint-disable-next-line eqeqeq
|
||||
if (savedListener == listener) {
|
||||
// found our listener
|
||||
found = true;
|
||||
// we just skip it
|
||||
} else {
|
||||
// other listeners should go back to original array
|
||||
newListeners.push(savedListener);
|
||||
}
|
||||
}
|
||||
|
||||
if (found) {
|
||||
this._listeners[event] = newListeners;
|
||||
} else {
|
||||
// something went wrong, lets just cleanup all listeners
|
||||
this._listeners[event] = [];
|
||||
}
|
||||
}
|
||||
|
||||
connect(port, host, callback) {
|
||||
console.log('connecting TOR socket...', host, port);
|
||||
(async () => {
|
||||
console.log('starting tor...');
|
||||
try {
|
||||
await tor.startIfNotStarted();
|
||||
} catch (e) {
|
||||
console.warn('Could not bootstrap TOR', e);
|
||||
await tor.stopIfRunning();
|
||||
this._passOnEvent('error', 'Could not bootstrap TOR');
|
||||
return false;
|
||||
}
|
||||
console.log('started tor');
|
||||
const iWillConnectISwear = tor.createTcpConnection({ target: host + ':' + port, connectionTimeout: 15000 }, (data, err) => {
|
||||
if (err) {
|
||||
console.log('TOR socket onData error: ', err);
|
||||
// this._passOnEvent('error', err);
|
||||
return;
|
||||
}
|
||||
this._passOnEvent('data', data);
|
||||
});
|
||||
|
||||
try {
|
||||
this._socket = await Promise.race([iWillConnectISwear, new Promise(resolve => setTimeout(resolve, 21000))]);
|
||||
} catch (e) {}
|
||||
|
||||
if (!this._socket) {
|
||||
console.log('connecting TOR socket failed'); // either sleep expired or connect threw an exception
|
||||
await tor.stopIfRunning();
|
||||
this._passOnEvent('error', 'connecting TOR socket failed');
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('TOR socket connected:', host, port);
|
||||
setTimeout(() => {
|
||||
this._passOnEvent('connect', true);
|
||||
callback();
|
||||
}, 1000);
|
||||
})();
|
||||
}
|
||||
|
||||
_passOnEvent(event, data) {
|
||||
this._listeners[event] = this._listeners[event] || [];
|
||||
for (const savedListener of this._listeners[event]) {
|
||||
savedListener(data);
|
||||
}
|
||||
}
|
||||
|
||||
emit(event, data) {}
|
||||
|
||||
end() {
|
||||
console.log('trying to close TOR socket');
|
||||
if (this._socket && this._socket.close) {
|
||||
console.log('trying to close TOR socket SUCCESS');
|
||||
return this._socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
destroy() {}
|
||||
|
||||
write(data) {
|
||||
if (this._socket && this._socket.write) {
|
||||
try {
|
||||
return this._socket.write(data);
|
||||
} catch (error) {
|
||||
console.log('this._socket.write() failed so we are issuing ERROR event', error);
|
||||
this._passOnEvent('error', error);
|
||||
}
|
||||
} else {
|
||||
console.log('TOR socket write error, socket not connected');
|
||||
this._passOnEvent('error', 'TOR socket not connected');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports.getDaemonStatus = async () => {
|
||||
try {
|
||||
return await tor.getDaemonStatus();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.stopIfRunning = async () => {
|
||||
try {
|
||||
Torsbee._testConn = false;
|
||||
return await tor.stopIfRunning();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.startIfNotStarted = async () => {
|
||||
try {
|
||||
return await tor.startIfNotStarted();
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.testSocket = async () => {
|
||||
const c = new Torsbee();
|
||||
return c.testSocket();
|
||||
};
|
||||
|
||||
module.exports.testHttp = async () => {
|
||||
const api = new Torsbee({
|
||||
baseURI: 'http://explorerzydxu5ecjrkwceayqybizmpjjznk5izmitf2modhcusuqlid.onion:80/',
|
||||
});
|
||||
const torResponse = await api.get('/api/tx/a84dbcf0d2550f673dda9331eea7cab86b645fd6e12049755c4b47bd238adce9', {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
const json = torResponse.body;
|
||||
if (json.txid !== 'a84dbcf0d2550f673dda9331eea7cab86b645fd6e12049755c4b47bd238adce9')
|
||||
throw new Error('TOR failure, got ' + JSON.stringify(torResponse));
|
||||
};
|
||||
|
||||
module.exports.Torsbee = Torsbee;
|
||||
module.exports.Socket = TorSocket;
|
@ -2,6 +2,7 @@ import { LegacyWallet } from './legacy-wallet';
|
||||
import Frisbee from 'frisbee';
|
||||
import bolt11 from 'bolt11';
|
||||
import { BitcoinUnit, Chain } from '../../models/bitcoinUnits';
|
||||
const torrific = require('../../blue_modules/torrific');
|
||||
|
||||
export class LightningCustodianWallet extends LegacyWallet {
|
||||
static type = 'lightningCustodianWallet';
|
||||
@ -77,6 +78,12 @@ export class LightningCustodianWallet extends LegacyWallet {
|
||||
this._api = new Frisbee({
|
||||
baseURI: this.baseURI,
|
||||
});
|
||||
|
||||
if (this.baseURI.indexOf('.onion') !== -1) {
|
||||
this._api = new torrific.Torsbee({
|
||||
baseURI: this.baseURI,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
accessTokenExpired() {
|
||||
@ -581,9 +588,14 @@ export class LightningCustodianWallet extends LegacyWallet {
|
||||
}
|
||||
|
||||
static async isValidNodeAddress(address) {
|
||||
const apiCall = new Frisbee({
|
||||
baseURI: address,
|
||||
});
|
||||
const isTor = address.indexOf('.onion') !== -1;
|
||||
const apiCall = isTor
|
||||
? new torrific.Torsbee({
|
||||
baseURI: address,
|
||||
})
|
||||
: new Frisbee({
|
||||
baseURI: address,
|
||||
});
|
||||
const response = await apiCall.get('/getinfo', {
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
|
@ -95,7 +95,7 @@
|
||||
6DEB4C3B254FBF4800E9F9AA /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEB4C3A254FBF4800E9F9AA /* Colors.swift */; };
|
||||
6DEB4C3C254FBF4800E9F9AA /* Colors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DEB4C3A254FBF4800E9F9AA /* Colors.swift */; };
|
||||
6DF25A9F249DB97E001D06F5 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6DF25A9E249DB97E001D06F5 /* LaunchScreen.storyboard */; };
|
||||
6DFC807024EA0B6C007B8700 /* EFQRCode in Frameworks */ = {isa = PBXBuildFile; productRef = 6DFC806F24EA0B6C007B8700 /* EFQRCode */; };
|
||||
6DFC807024EA0B6C007B8700 /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = 6DFC806F24EA0B6C007B8700 /* SwiftPackageProductDependency */; };
|
||||
6DFC807224EA2FA9007B8700 /* ViewQRCodefaceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DFC807124EA2FA9007B8700 /* ViewQRCodefaceController.swift */; };
|
||||
764B49B1420D4AEB8109BF62 /* libsqlite3.0.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 7B468CC34D5B41F3950078EF /* libsqlite3.0.tbd */; };
|
||||
782F075B5DD048449E2DECE9 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = B9D9B3A7B2CB4255876B67AF /* libz.tbd */; };
|
||||
@ -496,7 +496,7 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
6DFC807024EA0B6C007B8700 /* EFQRCode in Frameworks */,
|
||||
6DFC807024EA0B6C007B8700 /* BuildFile in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@ -996,7 +996,7 @@
|
||||
);
|
||||
name = "BlueWalletWatch Extension";
|
||||
packageProductDependencies = (
|
||||
6DFC806F24EA0B6C007B8700 /* EFQRCode */,
|
||||
6DFC806F24EA0B6C007B8700 /* SwiftPackageProductDependency */,
|
||||
);
|
||||
productName = "BlueWalletWatch Extension";
|
||||
productReference = B40D4E3C225841ED00428FCC /* BlueWalletWatch Extension.appex */;
|
||||
@ -1089,7 +1089,7 @@
|
||||
);
|
||||
mainGroup = 83CBB9F61A601CBA00E9B192;
|
||||
packageReferences = (
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */,
|
||||
6DFC806E24EA0B6C007B8700 /* RemoteSwiftPackageReference */,
|
||||
);
|
||||
productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */;
|
||||
projectDirPath = "";
|
||||
@ -1205,6 +1205,24 @@
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "export SENTRY_PROPERTIES=sentry.properties\nexport NODE_BINARY=node\n../node_modules/@sentry/cli/bin/sentry-cli react-native xcode ../node_modules/react-native/scripts/react-native-xcode.sh\n";
|
||||
};
|
||||
1D2D05C5A2622FFEA3FB0BEB /* [CP] Embed Pods Frameworks */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
inputPaths = (
|
||||
"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-frameworks.sh",
|
||||
"${PODS_ROOT}/../../node_modules/react-native-tor/ios/Libsifir_ios.framework",
|
||||
);
|
||||
name = "[CP] Embed Pods Frameworks";
|
||||
outputPaths = (
|
||||
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Libsifir_ios.framework",
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
shellPath = /bin/sh;
|
||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-BlueWallet/Pods-BlueWallet-frameworks.sh\"\n";
|
||||
showEnvVarsInLog = 0;
|
||||
};
|
||||
2130DE983D1D45AC8FC45F7E /* Upload Debug Symbols to Sentry */ = {
|
||||
isa = PBXShellScriptBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -1613,6 +1631,7 @@
|
||||
CURRENT_PROJECT_VERSION = 310;
|
||||
DEAD_CODE_STRIPPING = NO;
|
||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||
ENABLE_BITCODE = NO;
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"$(inherited)",
|
||||
@ -1621,7 +1640,7 @@
|
||||
);
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = BlueWallet/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -1661,10 +1680,11 @@
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 310;
|
||||
DEVELOPMENT_TEAM = A7W54YZ4WU;
|
||||
ENABLE_BITCODE = NO;
|
||||
"ENABLE_HARDENED_RUNTIME[sdk=macosx*]" = YES;
|
||||
HEADER_SEARCH_PATHS = "$(inherited)";
|
||||
INFOPLIST_FILE = BlueWallet/Info.plist;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 11.1;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
@ -2513,7 +2533,7 @@
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */ = {
|
||||
6DFC806E24EA0B6C007B8700 /* RemoteSwiftPackageReference */ = {
|
||||
isa = XCRemoteSwiftPackageReference;
|
||||
repositoryURL = "https://github.com/EFPrefix/EFQRCode.git";
|
||||
requirement = {
|
||||
@ -2524,9 +2544,9 @@
|
||||
/* End XCRemoteSwiftPackageReference section */
|
||||
|
||||
/* Begin XCSwiftPackageProductDependency section */
|
||||
6DFC806F24EA0B6C007B8700 /* EFQRCode */ = {
|
||||
6DFC806F24EA0B6C007B8700 /* SwiftPackageProductDependency */ = {
|
||||
isa = XCSwiftPackageProductDependency;
|
||||
package = 6DFC806E24EA0B6C007B8700 /* XCRemoteSwiftPackageReference "EFQRCode" */;
|
||||
package = 6DFC806E24EA0B6C007B8700 /* RemoteSwiftPackageReference */;
|
||||
productName = EFQRCode;
|
||||
};
|
||||
/* End XCSwiftPackageProductDependency section */
|
||||
|
@ -1,4 +1,4 @@
|
||||
platform :ios, '10.0'
|
||||
platform :ios, '11.1'
|
||||
workspace 'BlueWallet'
|
||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||
|
@ -274,6 +274,8 @@ PODS:
|
||||
- react-native-tcp-socket (3.7.1):
|
||||
- CocoaAsyncSocket
|
||||
- React
|
||||
- react-native-tor (0.1.7):
|
||||
- React
|
||||
- react-native-webview (11.3.1):
|
||||
- React-Core
|
||||
- react-native-widget-center (0.0.4):
|
||||
@ -452,6 +454,7 @@ DEPENDENCIES:
|
||||
- react-native-randombytes (from `../node_modules/react-native-randombytes`)
|
||||
- react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`)
|
||||
- react-native-tcp-socket (from `../node_modules/react-native-tcp-socket`)
|
||||
- react-native-tor (from `../node_modules/react-native-tor`)
|
||||
- react-native-webview (from `../node_modules/react-native-webview`)
|
||||
- react-native-widget-center (from `../node_modules/react-native-widget-center`)
|
||||
- React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`)
|
||||
@ -571,6 +574,8 @@ EXTERNAL SOURCES:
|
||||
:path: "../node_modules/react-native-safe-area-context"
|
||||
react-native-tcp-socket:
|
||||
:path: "../node_modules/react-native-tcp-socket"
|
||||
react-native-tor:
|
||||
:path: "../node_modules/react-native-tor"
|
||||
react-native-webview:
|
||||
:path: "../node_modules/react-native-webview"
|
||||
react-native-widget-center:
|
||||
@ -701,6 +706,7 @@ SPEC CHECKSUMS:
|
||||
react-native-randombytes: 45ae693012df24c9a07a5e578b3b567c01468581
|
||||
react-native-safe-area-context: e471852c5ed67eea4b10c5d9d43c1cebae3b231d
|
||||
react-native-tcp-socket: 96a4f104cdcc9c6621aafe92937f163d88447c5b
|
||||
react-native-tor: 4f389f5719dad633542b57ea32744e954730e7ef
|
||||
react-native-webview: 30f048378c6cee522ed9bbbedbc34acb21e58188
|
||||
react-native-widget-center: 0f81d17beb163e7fb5848b06754d7d277fe7d99a
|
||||
React-RCTActionSheet: 89a0ca9f4a06c1f93c26067af074ccdce0f40336
|
||||
@ -744,6 +750,6 @@ SPEC CHECKSUMS:
|
||||
Yoga: 4bd86afe9883422a7c4028c00e34790f560923d6
|
||||
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
|
||||
|
||||
PODFILE CHECKSUM: 070fb164a2dfb94605ea7862fd9b1c25e95ff046
|
||||
PODFILE CHECKSUM: 8fb37a9b658fd4d511d97e40f40e2d511092b261
|
||||
|
||||
COCOAPODS: 1.10.1
|
||||
|
@ -276,6 +276,7 @@
|
||||
"electrum_history": "Server history",
|
||||
"electrum_reset_to_default": "Are you sure to want to reset your Electrum settings to default?",
|
||||
"electrum_clear": "Clear",
|
||||
"tor_supported": "TOR supported",
|
||||
"encrypt_decrypt": "Decrypt Storage",
|
||||
"encrypt_decrypt_q": "Are you sure you want to decrypt your storage? This will allow your wallets to be accessed without a password.",
|
||||
"encrypt_del_uninstall": "Delete if BlueWallet is uninstalled",
|
||||
@ -296,6 +297,7 @@
|
||||
"lightning_error_lndhub_uri": "Not a valid LNDHub URI",
|
||||
"lightning_saved": "Your changes have been saved successfully.",
|
||||
"lightning_settings": "Lightning Settings",
|
||||
"tor_settings": "TOR Settings",
|
||||
"lightning_settings_explain": "To connect to your own LND node, please install LNDHub and put its URL here in settings. Leave blank to use BlueWallet’s LNDHub (lndhub.io). Wallets created after saving changes will connect to the specified LNDHub.",
|
||||
"network": "Network",
|
||||
"network_broadcast": "Broadcast Transaction",
|
||||
|
27
package-lock.json
generated
27
package-lock.json
generated
@ -6075,6 +6075,11 @@
|
||||
"@sinonjs/commons": "^1.7.0"
|
||||
}
|
||||
},
|
||||
"@types/async": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/async/-/async-3.2.6.tgz",
|
||||
"integrity": "sha512-ZkrXnZLC1mc4b9QLKaSrsxV4oxTRs10OI2kgSApT8G0v1jrmqppSHUVQ15kLorzsFBTjvf7OKF4kAibuuNQ+xA=="
|
||||
},
|
||||
"@types/babel__core": {
|
||||
"version": "7.1.9",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz",
|
||||
@ -9120,8 +9125,8 @@
|
||||
"integrity": "sha512-rQItBTFnol20HaaLm26UgSUduX7iGerwW7pEYX17MB1tI6LzFajiLV7iZ7LVcUcsN/7HrZUoCLrBauChy/IqEg=="
|
||||
},
|
||||
"electrum-client": {
|
||||
"version": "git+ssh://git@github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030",
|
||||
"from": "electrum-client@git+https://github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030"
|
||||
"version": "git+https://github.com/BlueWallet/rn-electrum-client.git#99ebcc649d91a8dc39bea7964b02dd9ead464aa4",
|
||||
"from": "git+https://github.com/BlueWallet/rn-electrum-client.git#99ebcc649d91a8dc39bea7964b02dd9ead464aa4"
|
||||
},
|
||||
"electrum-mnemonic": {
|
||||
"version": "2.0.0",
|
||||
@ -18815,6 +18820,22 @@
|
||||
"version": "git+ssh://git@github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
|
||||
"from": "react-native-tooltip@git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6"
|
||||
},
|
||||
"react-native-tor": {
|
||||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/react-native-tor/-/react-native-tor-0.1.7.tgz",
|
||||
"integrity": "sha512-jIIo4at/cD/Hy6y/Ae+509XeJ/47AvAoRBJBOV0CsmpAh6WacBmIrkiEBv1SUqh2pkbXG6YGn3CRJFowbc5UYw==",
|
||||
"requires": {
|
||||
"@types/async": "^3.2.6",
|
||||
"async": "^3.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"async": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
|
||||
"integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-native-vector-icons": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-vector-icons/-/react-native-vector-icons-7.1.0.tgz",
|
||||
@ -19656,7 +19677,7 @@
|
||||
}
|
||||
},
|
||||
"scryptsy": {
|
||||
"version": "file:blue_modules/scryptsy",
|
||||
"version": "file:https:/registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz",
|
||||
"integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w=="
|
||||
},
|
||||
"secp256k1": {
|
||||
|
@ -102,7 +102,7 @@
|
||||
"dayjs": "1.10.4",
|
||||
"detox": "18.8.1",
|
||||
"ecurve": "1.0.6",
|
||||
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#f9a827e724a5a2e578fdfdb483f83793af55b030",
|
||||
"electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#99ebcc649d91a8dc39bea7964b02dd9ead464aa4",
|
||||
"electrum-mnemonic": "2.0.0",
|
||||
"eslint-config-prettier": "6.14.0",
|
||||
"eslint-config-standard": "14.1.1",
|
||||
@ -164,6 +164,7 @@
|
||||
"react-native-svg": "12.1.0",
|
||||
"react-native-tcp-socket": "3.7.1",
|
||||
"react-native-tooltip": "git+https://github.com/BlueWallet/react-native-tooltip.git#d369e7ece09e4dec73873f1cfeac83e9d35294a6",
|
||||
"react-native-tor": "0.1.7",
|
||||
"react-native-vector-icons": "7.1.0",
|
||||
"react-native-watch-connectivity": "1.0.3",
|
||||
"react-native-webview": "11.3.1",
|
||||
|
@ -10,6 +10,7 @@ const bitcoin = require('bitcoinjs-lib');
|
||||
const BlueCrypto = require('react-native-blue-crypto');
|
||||
const encryption = require('../blue_modules/encryption');
|
||||
const BlueElectrum = require('../blue_modules/BlueElectrum');
|
||||
const torrific = require('../blue_modules/torrific');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
center: {
|
||||
@ -46,6 +47,13 @@ export default class Selftest extends Component {
|
||||
|
||||
//
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
||||
await torrific.testHttp();
|
||||
await torrific.testSocket();
|
||||
} else {
|
||||
// skipping RN-specific test'
|
||||
}
|
||||
|
||||
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
||||
await BlueElectrum.ping();
|
||||
await BlueElectrum.waitTillConnected();
|
||||
|
@ -15,10 +15,12 @@ import loc, { formatBalance, formatBalanceWithoutSuffix } from '../../loc';
|
||||
import { BlueCurrentTheme } from '../../components/themes';
|
||||
import Notifications from '../../blue_modules/notifications';
|
||||
import { BlueStorageContext } from '../../blue_modules/storage-context';
|
||||
import { Psbt } from 'bitcoinjs-lib';
|
||||
const currency = require('../../blue_modules/currency');
|
||||
const BlueElectrum = require('../../blue_modules/BlueElectrum');
|
||||
const Bignumber = require('bignumber.js');
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
const torrific = require('../../blue_modules/torrific');
|
||||
|
||||
export default class Confirm extends Component {
|
||||
static contextType = BlueStorageContext;
|
||||
@ -69,11 +71,38 @@ export default class Confirm extends Component {
|
||||
} else {
|
||||
const wallet = new PayjoinTransaction(this.state.psbt, txHex => this.broadcast(txHex), this.state.fromWallet);
|
||||
const paymentScript = this.getPaymentScript();
|
||||
const payjoinClient = new PayjoinClient({
|
||||
paymentScript,
|
||||
wallet,
|
||||
payjoinUrl: this.state.payjoinUrl,
|
||||
});
|
||||
let payjoinClient;
|
||||
if (this.state.payjoinUrl.includes('.onion')) {
|
||||
console.warn('trying TOR....');
|
||||
const payjoinUrl = this.state.payjoinUrl;
|
||||
// working through TOR - crafting custom requester that will handle TOR http request
|
||||
const customPayjoinRequester = {
|
||||
requestPayjoin: async function (psbt: Psbt) {
|
||||
console.warn('requesting payjoin with psbt:', psbt.toBase64());
|
||||
const api = new torrific.Torsbee();
|
||||
const torResponse = await api.post(payjoinUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain',
|
||||
},
|
||||
body: psbt.toBase64(),
|
||||
});
|
||||
console.warn('got torResponse.body');
|
||||
if (!torResponse.body) throw new Error('TOR failure, got ' + JSON.stringify(torResponse));
|
||||
return Psbt.fromBase64(torResponse.body);
|
||||
},
|
||||
};
|
||||
payjoinClient = new PayjoinClient({
|
||||
paymentScript,
|
||||
wallet,
|
||||
payjoinRequester: customPayjoinRequester,
|
||||
});
|
||||
} else {
|
||||
payjoinClient = new PayjoinClient({
|
||||
paymentScript,
|
||||
wallet,
|
||||
payjoinUrl: this.state.payjoinUrl,
|
||||
});
|
||||
}
|
||||
await payjoinClient.run();
|
||||
const payjoinPsbt = wallet.getPayjoinPsbt();
|
||||
if (payjoinPsbt) {
|
||||
|
@ -13,6 +13,10 @@ const NetworkSettings = () => {
|
||||
navigate('ElectrumSettings');
|
||||
};
|
||||
|
||||
const navigateToTorSettings = () => {
|
||||
navigate('TorSettings');
|
||||
};
|
||||
|
||||
const navigateToLightningSettings = () => {
|
||||
navigate('LightningSettings');
|
||||
};
|
||||
@ -30,6 +34,7 @@ const NetworkSettings = () => {
|
||||
chevron
|
||||
/>
|
||||
)}
|
||||
<BlueListItem title={loc.settings.tor_settings} onPress={navigateToTorSettings} testID="TorSettings" chevron />
|
||||
</ScrollView>
|
||||
</SafeBlueArea>
|
||||
);
|
||||
|
@ -220,9 +220,11 @@ export default class ElectrumSettings extends Component {
|
||||
<BlueCard>
|
||||
<BlueText style={styles.status}>{loc.settings.electrum_status}</BlueText>
|
||||
<View style={styles.connectWrap}>
|
||||
<View style={[styles.container, this.state.config.status === 1 ? styles.containerConnected : styles.containerDisconnected]}>
|
||||
<BlueText style={this.state.config.status === 1 ? styles.textConnected : styles.textDisconnected}>
|
||||
{this.state.config.status === 1 ? loc.settings.electrum_connected : loc.settings.electrum_connected_not}
|
||||
<View
|
||||
style={[styles.container, this.state.config.connected === 1 ? styles.containerConnected : styles.containerDisconnected]}
|
||||
>
|
||||
<BlueText style={this.state.config.connected === 1 ? styles.textConnected : styles.textDisconnected}>
|
||||
{this.state.config.connected === 1 ? loc.settings.electrum_connected : loc.settings.electrum_connected_not}
|
||||
</BlueText>
|
||||
</View>
|
||||
</View>
|
||||
@ -287,6 +289,7 @@ export default class ElectrumSettings extends Component {
|
||||
testID="SSLPortInput"
|
||||
/>
|
||||
</View>
|
||||
<BlueText style={styles.torSupported}>{loc.settings.tor_supported}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} onPress={this.importScan} />
|
||||
<BlueSpacing20 />
|
||||
@ -402,4 +405,7 @@ const styles = StyleSheet.create({
|
||||
borderBottomColor: BlueCurrentTheme.colors.formBorder,
|
||||
borderBottomWidth: 1,
|
||||
},
|
||||
torSupported: {
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
@ -37,6 +37,9 @@ const styles = StyleSheet.create({
|
||||
backgroundColor: 'transparent',
|
||||
flexDirection: I18nManager.isRTL ? 'row-reverse' : 'row',
|
||||
},
|
||||
torSupported: {
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
||||
const LightningSettings = () => {
|
||||
@ -144,6 +147,8 @@ const LightningSettings = () => {
|
||||
/>
|
||||
</View>
|
||||
|
||||
<BlueText style={[styles.torSupported]}>{loc.settings.tor_supported}</BlueText>
|
||||
<BlueSpacing20 />
|
||||
<BlueButtonLink title={loc.wallets.import_scan_qr} testID="ImportScan" onPress={importScan} />
|
||||
<BlueSpacing20 />
|
||||
{isLoading ? <BlueLoading /> : <BlueButton testID="Save" onPress={save} title={loc.settings.save} />}
|
||||
|
93
screen/settings/torSettings.js
Normal file
93
screen/settings/torSettings.js
Normal file
@ -0,0 +1,93 @@
|
||||
/* global alert */
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { View, StyleSheet } from 'react-native';
|
||||
|
||||
import navigationStyle from '../../components/navigationStyle';
|
||||
import { BlueButton, BlueCard, BlueLoading, BlueSpacing20, BlueText, SafeBlueArea } from '../../BlueComponents';
|
||||
import loc from '../../loc';
|
||||
|
||||
const torrific = require('../../blue_modules/torrific');
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
torSupported: {
|
||||
color: '#81868e',
|
||||
},
|
||||
});
|
||||
|
||||
const TorSettings = () => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [daemonStatus, setDaemonStatus] = useState('');
|
||||
|
||||
const updateStatus = async () => {
|
||||
const status = await torrific.getDaemonStatus();
|
||||
setDaemonStatus(status);
|
||||
};
|
||||
|
||||
const startIfNotStarted = async () => {
|
||||
await torrific.startIfNotStarted();
|
||||
};
|
||||
|
||||
const stopIfRunning = async () => {
|
||||
await torrific.stopIfRunning();
|
||||
};
|
||||
|
||||
const testSocket = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await torrific.testSocket();
|
||||
alert('OK');
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const testHttp = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await torrific.testHttp();
|
||||
alert('OK');
|
||||
} catch (error) {
|
||||
alert(error.message);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const interval = setInterval(updateStatus, 1000);
|
||||
return () => {
|
||||
clearInterval(interval);
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<View style={[styles.root]}>
|
||||
<BlueLoading />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SafeBlueArea>
|
||||
<BlueCard>
|
||||
<BlueText>Daemon Status: {daemonStatus}</BlueText>
|
||||
</BlueCard>
|
||||
|
||||
<BlueButton title="start" onPress={startIfNotStarted} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="stop" onPress={stopIfRunning} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test socket" onPress={testSocket} />
|
||||
<BlueSpacing20 />
|
||||
<BlueButton title="test http" onPress={testHttp} />
|
||||
</SafeBlueArea>
|
||||
);
|
||||
};
|
||||
|
||||
TorSettings.navigationOptions = navigationStyle({}, opts => ({ ...opts, title: loc.settings.tor_settings }));
|
||||
|
||||
export default TorSettings;
|
@ -1,6 +1,6 @@
|
||||
const bitcoin = require('bitcoinjs-lib');
|
||||
global.net = require('net');
|
||||
global.tls = require('tls');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
|
||||
const assert = require('assert');
|
||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = 150 * 1000;
|
||||
@ -19,7 +19,7 @@ describe('ElectrumClient', () => {
|
||||
const ElectrumClient = require('electrum-client');
|
||||
|
||||
for (const peer of hardcodedPeers) {
|
||||
const mainClient = new ElectrumClient(peer.ssl || peer.tcp, peer.host, peer.ssl ? 'tls' : 'tcp');
|
||||
const mainClient = new ElectrumClient(net, tls, peer.ssl || peer.tcp, peer.host, peer.ssl ? 'tls' : 'tcp');
|
||||
|
||||
try {
|
||||
await mainClient.connect();
|
||||
|
Loading…
Reference in New Issue
Block a user