diff --git a/react/src/actions/actions/nativeSyncInfo.js b/react/src/actions/actions/nativeSyncInfo.js index 867605310..94af944df 100644 --- a/react/src/actions/actions/nativeSyncInfo.js +++ b/react/src/actions/actions/nativeSyncInfo.js @@ -5,7 +5,6 @@ import { import { triggerToaster, getDebugLog, - toggleCoindDownModal } from '../actionCreators'; import Config from '../../config'; import { translate } from '../../translate/translate'; @@ -138,7 +137,7 @@ export function getSyncInfoNative(coin, skipDebug, skipRemote, suppressErrors) { { result: 'daemon is busy', error: null, - id: null + id: null, }, coin, true, @@ -146,16 +145,15 @@ export function getSyncInfoNative(coin, skipDebug, skipRemote, suppressErrors) { ) ); } else { - if (!json) { + if (!json || + json.indexOf('"code":-777') > -1) { let _kmdMainPassiveMode; try { _kmdMainPassiveMode = window.require('electron').remote.getCurrentWindow().kmdMainPassiveMode; } catch (e) {} - if (!_kmdMainPassiveMode) { - dispatch(nativeGetinfoFailureState()); - } else { + if (_kmdMainPassiveMode) { dispatch( triggerToaster( translate('API.KMD_PASSIVE_ERROR'), @@ -171,7 +169,6 @@ export function getSyncInfoNative(coin, skipDebug, skipRemote, suppressErrors) { } else { dispatch(getDebugLog('komodo', 50, coin)); } - dispatch(toggleCoindDownModal(true)); } else { json = JSON.parse(json); } diff --git a/react/src/actions/actions/settings.js b/react/src/actions/actions/settings.js index dfe0a429e..5db5ebf8d 100644 --- a/react/src/actions/actions/settings.js +++ b/react/src/actions/actions/settings.js @@ -386,4 +386,96 @@ export function resetAppConfig() { ); }) } +} + +export function coindGetStdout(chain) { + const _chain = chain === 'KMD' ? 'komodod' : chain; + + return new Promise((resolve, reject) => { + fetch(`http://127.0.0.1:${Config.agamaPort}/shepherd/coind/stdout?chain=${chain}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'coindGetStdout', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json); + }); + }); +} + +export function getWalletDatKeys(chain, keyMatchPattern) { + const _chain = chain === 'KMD' ? null : chain; + + return new Promise((resolve, reject) => { + fetch(keyMatchPattern ? `http://127.0.0.1:${Config.agamaPort}/shepherd/coindwalletkeys?chain=${_chain}&search=${keyMatchPattern}` : `http://127.0.0.1:${Config.agamaPort}/shepherd/coindwalletkeys?chain=${_chain}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .catch((error) => { + console.log(error); + dispatch( + triggerToaster( + 'getWalletDatKeys', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json); + }); + }); +} + +export function dumpPrivKey(coin, address) { + return new Promise((resolve, reject) => { + const payload = { + mode: null, + chain: coin, + cmd: 'dumpprivkey', + params: [ address ] + }; + + const _fetchConfig = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ 'payload': payload }), + }; + + fetch( + `http://127.0.0.1:${Config.agamaPort}/shepherd/cli`, + _fetchConfig + ) + .catch(function(error) { + console.log(error); + dispatch( + triggerToaster( + 'dumpPrivKey', + 'Error', + 'error' + ) + ); + }) + .then(response => response.json()) + .then(json => { + resolve(json.result ? json.result : json); + }) + }); } \ No newline at end of file diff --git a/react/src/components/addcoin/addcoin.js b/react/src/components/addcoin/addcoin.js index 43237f64c..1d42963f2 100644 --- a/react/src/components/addcoin/addcoin.js +++ b/react/src/components/addcoin/addcoin.js @@ -7,9 +7,11 @@ import { toggleAddcoinModal, triggerToaster, shepherdGetCoinList, - shepherdPostCoinList + shepherdPostCoinList, + toggleZcparamsFetchModal, } from '../../actions/actionCreators'; import Store from '../../store'; +import { zcashParamsCheckErrors } from '../../util/zcashParams'; import CoinSelectorsRender from './coin-selectors.render'; import AddCoinRender from './addcoin.render'; @@ -46,6 +48,43 @@ class AddCoin extends React.Component { this.toggleActionsMenu = this.toggleActionsMenu.bind(this); this.saveCoinSelection = this.saveCoinSelection.bind(this); this.loadCoinSelection = this.loadCoinSelection.bind(this); + this.verifyZcashParamsExist = this.verifyZcashParamsExist.bind(this); + } + + verifyZcashParamsExist(mode) { + return new Promise((resolve, reject) => { + if (Number(mode) === -1) { + const _res = window.require('electron').remote.getCurrentWindow().zcashParamsExist; + const __errors = zcashParamsCheckErrors(_res); + + if (__errors) { + window.require('electron').remote.getCurrentWindow().zcashParamsExistPromise() + .then((res) => { + const _errors = zcashParamsCheckErrors(res); + window.require('electron').remote.getCurrentWindow().zcashParamsExist = res; + + if (_errors) { + Store.dispatch( + triggerToaster( + _errors, + 'Komodod', + 'error', + false + ) + ); + Store.dispatch(toggleZcparamsFetchModal(true)); + resolve(false); + } else { + resolve(true); + } + }); + } else { + resolve(true); + } + } else { + resolve(true); + } + }); } saveCoinSelection() { @@ -206,23 +245,28 @@ class AddCoin extends React.Component { return; } - if (!_coin.daemonParam) { - Store.dispatch(addCoin( - coin, - _coin.mode, - )); - } else { - Store.dispatch(addCoin( - coin, - _coin.mode, - { type: _coin.daemonParam } - )); - } + this.verifyZcashParamsExist(_coin.mode) + .then((res) => { + if (res) { + if (!_coin.daemonParam) { + Store.dispatch(addCoin( + coin, + _coin.mode, + )); + } else { + Store.dispatch(addCoin( + coin, + _coin.mode, + { type: _coin.daemonParam } + )); + } - this.removeCoin(); - this.addNewItem(); + this.removeCoin(); + this.addNewItem(); - Store.dispatch(toggleAddcoinModal(false, false)); + Store.dispatch(toggleAddcoinModal(false, false)); + } + }); } dismiss() { diff --git a/react/src/components/dashboard/coinTile/coinTileItem.js b/react/src/components/dashboard/coinTile/coinTileItem.js index cbd3c6419..12b824143 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.js @@ -24,13 +24,14 @@ import { activeHandle, triggerToaster, shepherdRemoveCoin, + toggleCoindDownModal, } from '../../../actions/actionCreators'; import Store from '../../../store'; import Config from '../../../config'; import CoinTileItemRender from './coinTileItem.render'; -const SPV_DASHBOARD_UPDATE_TIMEOUT = 10000; +const SPV_DASHBOARD_UPDATE_TIMEOUT = 60000; const ACTIVE_HANDLE_TIMEOUT_COIND_NATIVE = 15000; const COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD = window.require('electron').remote.getCurrentWindow().appConfig.failedRPCAttemptsThreshold || 10; @@ -39,10 +40,31 @@ class CoinTileItem extends React.Component { super(); this.state = { appConfig: {}, + activeCoin: null, + activeCoinMode: null, + propsUpdatedCounter: 0, }; this.autoSetActiveCoin = this.autoSetActiveCoin.bind(this); } + openCoindDownModal() { + Store.dispatch(toggleCoindDownModal(true)); + } + + renderCoinConError(item) { + if (this.props.ActiveCoin.getinfoFetchFailures >= COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD && + (this.props.ActiveCoin.mode === 'native' && + this.props.ActiveCoin.coin === this.state.activeCoin && + this.props.ActiveCoin.coin === item.coin && + this.state.activeCoin === item.coin && + this.state.activeCoinMode === 'native' && + this.props.ActiveCoin.mode === this.state.activeCoinMode && + this.state.propsUpdatedCounter > 1) || + (this.props.ActiveCoin.coins && this.props.ActiveCoin.coins[item.coin]) && this.props.ActiveCoin.coins[item.coin].getinfoFetchFailures >= COIND_DOWN_MODAL_FETCH_FAILURES_THRESHOLD) { + return true; + } + } + renderStopCoinButton() { if (this.props.Main && this.props.Main.coins && @@ -87,11 +109,11 @@ class CoinTileItem extends React.Component { _coinMode[coin] = mode; }); - if (_coinMode['KMD'] && - _coinMode['KMD'] === 'native') { + if (_coinMode.KMD && + _coinMode.KMD === 'native') { _coin = 'KMD'; _mode = 'native'; - } else if (_coinMode['KMD'] && _coinMode['KMD'] === 'spv') { + } else if (_coinMode.KMD && _coinMode.KMD === 'spv') { _coin = 'KMD'; _mode = 'spv'; } @@ -271,6 +293,13 @@ class CoinTileItem extends React.Component { Store.dispatch(electrumServerChanged(false)); }, 100); } + + this.setState({ + activeCoin: props.ActiveCoin.coin, + activeCoinMode: props.ActiveCoin.mode, + // prevent native con error icon flashing on coin switch + propsUpdatedCounter: this.state.activeCoin === props.ActiveCoin.coin && this.state.activeCoinMode === props.ActiveCoin.mode ? this.state.propsUpdatedCounter + 1 : 0, + }); } render() { @@ -282,6 +311,7 @@ const mapStateToProps = (state) => { return { ActiveCoin: { coin: state.ActiveCoin.coin, + coins: state.ActiveCoin.coins, mode: state.ActiveCoin.mode, addresses: state.ActiveCoin.addresses, mainBasiliskAddress: state.ActiveCoin.mainBasiliskAddress, diff --git a/react/src/components/dashboard/coinTile/coinTileItem.render.js b/react/src/components/dashboard/coinTile/coinTileItem.render.js index 08578f0e6..333e5e107 100644 --- a/react/src/components/dashboard/coinTile/coinTileItem.render.js +++ b/react/src/components/dashboard/coinTile/coinTileItem.render.js @@ -34,7 +34,7 @@ const CoinTileItemRender = function() { this.removeCoin(item.coin, item.mode) } title="Remove" - className={ 'icon fa-plus-circle ' + (item.mode === 'spv' ? 'coind-remove-icon coind-remove-icon-spv' : 'coind-remove-icon') }> + className={ 'icon fa-plus-circle coind-remove-icon' + (item.mode === 'spv' ? ' coind-remove-icon-spv' : '') }> } { this.props.Dashboard && this.props.Dashboard.electrumCoins && @@ -45,6 +45,12 @@ const CoinTileItemRender = function() { title={ translate('SETTINGS.SPV_SINGLE_SERVER_NOTICE') } className="icon fa-info-circle icon-spv-connection-warning"> } + { this.renderCoinConError(item) && + + } ); }; diff --git a/react/src/components/dashboard/coindDownModal/coindDownModal.js b/react/src/components/dashboard/coindDownModal/coindDownModal.js index c538ba327..d43de7f8f 100644 --- a/react/src/components/dashboard/coindDownModal/coindDownModal.js +++ b/react/src/components/dashboard/coindDownModal/coindDownModal.js @@ -1,6 +1,10 @@ import React from 'react'; import { connect } from 'react-redux'; -import { toggleCoindDownModal } from '../../../actions/actionCreators'; +import { + toggleCoindDownModal, + coindGetStdout, + getDebugLog, +} from '../../../actions/actionCreators'; import Store from '../../../store'; import CoindDownModalRender from './coindDownModal.render'; @@ -12,10 +16,43 @@ class CoindDownModal extends React.Component { super(); this.state = { display: false, - debugLogCrash: null, kmdMainPassiveMode: false, + coindStdOut: 'Loading...', + toggleDebugLog: true, }; this.dismiss = this.dismiss.bind(this); + this.getCoindGetStdout = this.getCoindGetStdout.bind(this); + this.toggleDebugLog = this.toggleDebugLog.bind(this); + this.refreshDebugLog = this.refreshDebugLog.bind(this); + } + + refreshDebugLog() { + const _coin = this.props.ActiveCoin.coin; + + if (!this.state.toggleDebugLog) { + if (_coin === 'KMD') { + Store.dispatch(getDebugLog('komodo', 50)); + } else { + Store.dispatch(getDebugLog('komodo', 50, _coin)); + } + } else { + this.getCoindGetStdout(); + } + } + + toggleDebugLog() { + this.setState({ + toggleDebugLog: !this.state.toggleDebugLog, + }); + } + + getCoindGetStdout() { + coindGetStdout(this.props.ActiveCoin.coin) + .then((res) => { + this.setState({ + coindStdOut: res.msg === 'success' ? res.result : `Error reading ${this.props.ActiveCoin.coin} stdout`, + }); + }); } dismiss() { @@ -39,6 +76,10 @@ class CoindDownModal extends React.Component { this.setState(Object.assign({}, this.state, { display: nextProps.displayCoindDownModal, })); + + if (nextProps.displayCoindDownModal) { + this.getCoindGetStdout(); + } } } diff --git a/react/src/components/dashboard/coindDownModal/coindDownModal.render.js b/react/src/components/dashboard/coindDownModal/coindDownModal.render.js index bbd5eb03b..e5acc9705 100644 --- a/react/src/components/dashboard/coindDownModal/coindDownModal.render.js +++ b/react/src/components/dashboard/coindDownModal/coindDownModal.render.js @@ -27,12 +27,36 @@ const CoindDownModalRender = function() {