From cd04a2afbbd6edb2502281be8a012f11849c5b88 Mon Sep 17 00:00:00 2001 From: Overtorment Date: Fri, 27 Mar 2020 19:18:14 +0000 Subject: [PATCH] REF: electrum android reconnect improvements --- BlueElectrum.js | 26 +++++++++++++----- package-lock.json | 4 +-- package.json | 2 +- screen/settings/electrumSettings.js | 38 ++++++++++++++++++++------ tests/integration/BlueElectrum.test.js | 8 ++++++ 5 files changed, 59 insertions(+), 19 deletions(-) diff --git a/BlueElectrum.js b/BlueElectrum.js index 811eeaab..31b5f0e0 100644 --- a/BlueElectrum.js +++ b/BlueElectrum.js @@ -1,4 +1,5 @@ import AsyncStorage from '@react-native-community/async-storage'; +import { Platform } from 'react-native'; import { AppStorage } from './class'; const bitcoin = require('bitcoinjs-lib'); const ElectrumClient = require('electrum-client'); @@ -38,11 +39,19 @@ async function connectMain() { console.log('begin connection:', JSON.stringify(usingPeer)); mainClient = new ElectrumClient(usingPeer.ssl || usingPeer.tcp, usingPeer.host, usingPeer.ssl ? 'tls' : 'tcp'); mainClient.onError = function(e) { - console.log('ElectrumClient error: ' + 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 + // code which does connection retries + mainClient.close(); + mainConnected = false; + setTimeout(connectMain, 500); + console.warn('reconnecting after socket error'); + return; + } mainConnected = false; }; - await mainClient.connect(); - const ver = await mainClient.server_version('2.7.11', '1.4'); + const ver = await mainClient.initElectrum({ client: 'bluewallet', version: '1.4' }); if (ver && ver[0]) { console.log('connected to ', ver); mainConnected = true; @@ -56,9 +65,7 @@ async function connectMain() { if (!mainConnected) { console.log('retry'); - mainClient.keepAlive = () => {}; // dirty hack to make it stop reconnecting - mainClient.reconnect = () => {}; // dirty hack to make it stop reconnecting - mainClient.close(); + mainClient.close && mainClient.close(); setTimeout(connectMain, 500); } } @@ -132,7 +139,7 @@ module.exports.getConfig = async function() { return { host: mainClient.host, port: mainClient.port, - status: mainClient.status, + status: mainClient.status && mainConnected ? 1 : 0, }; }; @@ -373,6 +380,11 @@ module.exports.estimateFee = async function(numberOfBlocks) { ); }; +module.exports.serverFeatures = async function() { + if (!mainClient) throw new Error('Electrum client is not connected'); + return mainClient.server_features(); +}; + module.exports.broadcast = async function(hex) { if (!mainClient) throw new Error('Electrum client is not connected'); try { diff --git a/package-lock.json b/package-lock.json index 8a6c3d8a..5e0fd0e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4374,8 +4374,8 @@ "integrity": "sha512-NK9DBBYEBb5f9D7zXI0hiE941gq3wkBeQmXs1ingigA/jnTg5mhwY2Z5egwA+ZI8OLGKCx0h1Cl8/xeuIBuLlg==" }, "electrum-client": { - "version": "git+https://github.com/BlueWallet/rn-electrum-client.git#4bbd13f3c2b9ae683ee8ec064d770be1f76b23a7", - "from": "git+https://github.com/BlueWallet/rn-electrum-client.git#4bbd13f3c2b9ae683ee8ec064d770be1f76b23a7" + "version": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d", + "from": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d" }, "elliptic": { "version": "6.5.2", diff --git a/package.json b/package.json index e0f0dc4d..8471e3c3 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "crypto-js": "3.1.9-1", "dayjs": "1.8.20", "ecurve": "1.0.6", - "electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#4bbd13f3c2b9ae683ee8ec064d770be1f76b23a7", + "electrum-client": "git+https://github.com/BlueWallet/rn-electrum-client.git#2a5bb11dd9a8d89f328049d9ed59bce49d88a15d", "eslint-config-prettier": "6.10.0", "eslint-config-standard": "12.0.0", "eslint-config-standard-react": "7.0.2", diff --git a/screen/settings/electrumSettings.js b/screen/settings/electrumSettings.js index 2cc9547c..8ad33fef 100644 --- a/screen/settings/electrumSettings.js +++ b/screen/settings/electrumSettings.js @@ -3,7 +3,16 @@ import React, { Component } from 'react'; import { View, TextInput } from 'react-native'; import { AppStorage } from '../../class'; import AsyncStorage from '@react-native-community/async-storage'; -import { BlueLoading, BlueSpacing20, BlueButton, SafeBlueArea, BlueCard, BlueNavigationStyle, BlueText } from '../../BlueComponents'; +import { + BlueLoading, + BlueSpacing20, + BlueButton, + SafeBlueArea, + BlueCard, + BlueNavigationStyle, + BlueText, + BlueButtonLink, +} from '../../BlueComponents'; import PropTypes from 'prop-types'; let loc = require('../../loc'); let BlueElectrum = require('../../BlueElectrum'); @@ -39,23 +48,32 @@ export default class ElectrumSettings extends Component { }); } + checkServer = async () => { + this.setState({ isLoading: true }, async () => { + const features = await BlueElectrum.serverFeatures(); + alert(JSON.stringify(features, null, 2)); + this.setState({ isLoading: false }); + }); + }; + save = () => { + const host = this.state.host ? this.state.host : ''; + const port = this.state.port ? this.state.port : ''; + const sslPort = this.state.sslPort ? this.state.sslPort : ''; + this.setState({ isLoading: true }, async () => { - this.state.host = this.state.host ? this.state.host : ''; - this.state.port = this.state.port ? this.state.port : ''; - this.state.sslPort = this.state.sslPort ? this.state.sslPort : ''; try { - if (!this.state.host && !this.state.port && !this.state.sslPort) { + if (!host && !port && !sslPort) { await AsyncStorage.setItem(AppStorage.ELECTRUM_HOST, ''); await AsyncStorage.setItem(AppStorage.ELECTRUM_TCP_PORT, ''); await AsyncStorage.setItem(AppStorage.ELECTRUM_SSL_PORT, ''); alert('Your changes have been saved successfully. Restart may be required for changes to take effect.'); - } else if (!(await BlueElectrum.testConnection(this.state.host, this.state.port, this.state.sslPort))) { + } else if (!(await BlueElectrum.testConnection(host, port, sslPort))) { alert("Can't connect to provided Electrum server"); } else { - await AsyncStorage.setItem(AppStorage.ELECTRUM_HOST, this.state.host); - await AsyncStorage.setItem(AppStorage.ELECTRUM_TCP_PORT, this.state.port); - await AsyncStorage.setItem(AppStorage.ELECTRUM_SSL_PORT, this.state.sslPort); + await AsyncStorage.setItem(AppStorage.ELECTRUM_HOST, host); + await AsyncStorage.setItem(AppStorage.ELECTRUM_TCP_PORT, port); + await AsyncStorage.setItem(AppStorage.ELECTRUM_SSL_PORT, sslPort); alert('Your changes have been saved successfully. Restart may be required for changes to take effect.'); } } catch (_) {} @@ -157,6 +175,8 @@ export default class ElectrumSettings extends Component { Host: {this.state.config.host} Port: {this.state.config.port} Connected: {(this.state.config.status === 1 && 'Yes') || 'No'} + + {this.state.isLoading ? : } ); diff --git a/tests/integration/BlueElectrum.test.js b/tests/integration/BlueElectrum.test.js index e95a5bdd..5680ca10 100644 --- a/tests/integration/BlueElectrum.test.js +++ b/tests/integration/BlueElectrum.test.js @@ -31,6 +31,14 @@ describe('BlueElectrum', () => { assert.ok((await BlueElectrum.estimateFee(1)) > 1); }); + it('ElectrumClient can request server features', async () => { + const features = await BlueElectrum.serverFeatures(); + // console.warn({features}); + assert.ok(features.server_version); + assert.ok(features.protocol_min); + assert.ok(features.protocol_max); + }); + it('BlueElectrum can do getBalanceByAddress()', async function() { let address = '3GCvDBAktgQQtsbN6x5DYiQCMmgZ9Yk8BK'; let balance = await BlueElectrum.getBalanceByAddress(address);