diff --git a/client/core/account.go b/client/core/account.go index 0ec2051a95..7df7cce7b7 100644 --- a/client/core/account.go +++ b/client/core/account.go @@ -73,7 +73,7 @@ func (c *Core) ToggleAccountStatus(pw []byte, addr string, disable bool) error { } if dc.hasUnspentBond() { - c.log.Warnf("Disabling dex server with unspent bonds. Bonds will be refunded when expired.") + c.log.Info("Disabling dex server with unspent bonds. Bonds will be refunded when expired.") } } @@ -86,16 +86,18 @@ func (c *Core) ToggleAccountStatus(pw []byte, addr string, disable bool) error { dc.acct.toggleAccountStatus(true) c.stopDEXConnection(dc) } else { - acct, err := c.db.Account(addr) + acctInfo, err := c.db.Account(addr) if err != nil { return err } - if !c.connectAccount(acct) { - c.log.Errorf("Failed to establish connection to %s (will retry)", addr) + dc, err := c.connectDEX(acctInfo) + if err != nil { + c.log.Errorf("Trouble establishing connection to %s (will retry). Error: %v", acctInfo.Host, err) } - - c.initializeDEXConnections(crypter) + // Connected or not, add dex connection to the connections map. + c.addDexConnection(dc) + c.initializeDEXConnection(dc, crypter) } return nil @@ -215,7 +217,7 @@ func (c *Core) AccountImport(pw []byte, acct *Account, bonds []*db.Bond) error { return err } c.addDexConnection(dc) - c.initializeDEXConnections(crypter) + c.initializeDEXConnection(dc, crypter) return nil } @@ -282,7 +284,7 @@ func (c *Core) AccountImport(pw []byte, acct *Account, bonds []*db.Bond) error { return err } c.addDexConnection(dc) - c.initializeDEXConnections(crypter) + c.initializeDEXConnection(dc, crypter) return nil } diff --git a/client/core/core.go b/client/core/core.go index ba537d10bd..ef36990166 100644 --- a/client/core/core.go +++ b/client/core/core.go @@ -5126,74 +5126,80 @@ func (c *Core) initializeDEXConnections(crypter encrypt.Crypter) { var wg sync.WaitGroup conns := c.dexConnections() for _, dc := range conns { - if dc.acct.isViewOnly() { - continue // don't attempt authDEX for view-only conn - } + wg.Add(1) + go func(dc *dexConnection) { + defer wg.Done() + c.initializeDEXConnection(dc, crypter) + }(dc) + } - // Unlock before checking auth and continuing, because if the user - // logged out and didn't shut down, the account is still authed, but - // locked, and needs unlocked. - err := dc.acct.unlock(crypter) - if err != nil { - subject, details := c.formatDetails(TopicAccountUnlockError, dc.acct.host, err) - c.notify(newFeePaymentNote(TopicAccountUnlockError, subject, details, db.ErrorLevel, dc.acct.host)) // newDEXAuthNote? - continue - } + wg.Wait() +} - if dc.acct.isDisabled() { - continue // For disabled account, we only want dc.acct.unlock above to initialize the account ID. - } +// initializeDEXConnection connects to the DEX server in the conns map and +// authenticates the connection. +func (c *Core) initializeDEXConnection(dc *dexConnection, crypter encrypt.Crypter) { + if dc.acct.isViewOnly() { + return // don't attempt authDEX for view-only conn + } - // Unlock the bond wallet if a target tier is set. - if bondAssetID, targetTier, maxBondedAmt := dc.bondOpts(); targetTier > 0 { - c.log.Debugf("Preparing %s wallet to maintain target tier of %d for %v, bonding limit %v", - unbip(bondAssetID), targetTier, dc.acct.host, maxBondedAmt) - wallet, exists := c.wallet(bondAssetID) - if !exists || !wallet.connected() { // connectWallets already run, just fail - subject, details := c.formatDetails(TopicBondWalletNotConnected, unbip(bondAssetID)) - var w *WalletState - if exists { - w = wallet.state() - } - c.notify(newWalletConfigNote(TopicBondWalletNotConnected, subject, details, db.ErrorLevel, w)) - } else if !wallet.unlocked() { - err = wallet.Unlock(crypter) - if err != nil { - subject, details := c.formatDetails(TopicWalletUnlockError, dc.acct.host, err) - c.notify(newFeePaymentNote(TopicWalletUnlockError, subject, details, db.ErrorLevel, dc.acct.host)) - } - } - } + // Unlock before checking auth and continuing, because if the user + // logged out and didn't shut down, the account is still authed, but + // locked, and needs unlocked. + err := dc.acct.unlock(crypter) + if err != nil { + subject, details := c.formatDetails(TopicAccountUnlockError, dc.acct.host, err) + c.notify(newFeePaymentNote(TopicAccountUnlockError, subject, details, db.ErrorLevel, dc.acct.host)) // newDEXAuthNote? + return + } - if dc.acct.authed() { // should not be possible with newly idempotent login, but there's AccountImport... - continue // authDEX already done + if dc.acct.isDisabled() { + return // For disabled account, we only want dc.acct.unlock above to initialize the account ID. + } + + // Unlock the bond wallet if a target tier is set. + if bondAssetID, targetTier, maxBondedAmt := dc.bondOpts(); targetTier > 0 { + c.log.Debugf("Preparing %s wallet to maintain target tier of %d for %v, bonding limit %v", + unbip(bondAssetID), targetTier, dc.acct.host, maxBondedAmt) + wallet, exists := c.wallet(bondAssetID) + if !exists || !wallet.connected() { // connectWallets already run, just fail + subject, details := c.formatDetails(TopicBondWalletNotConnected, unbip(bondAssetID)) + var w *WalletState + if exists { + w = wallet.state() + } + c.notify(newWalletConfigNote(TopicBondWalletNotConnected, subject, details, db.ErrorLevel, w)) + } else if !wallet.unlocked() { + err = wallet.Unlock(crypter) + if err != nil { + subject, details := c.formatDetails(TopicWalletUnlockError, dc.acct.host, err) + c.notify(newFeePaymentNote(TopicWalletUnlockError, subject, details, db.ErrorLevel, dc.acct.host)) + } } + } - // Pending bonds will be handled by authDEX. Expired bonds will be - // refunded by rotateBonds. + if dc.acct.authed() { // should not be possible with newly idempotent login, but there's AccountImport... + return // authDEX already done + } - // If the connection is down, authDEX will fail on Send. - if dc.IsDown() { - c.log.Warnf("Connection to %v not available for authorization. "+ - "It will automatically authorize when it connects.", dc.acct.host) - subject, details := c.formatDetails(TopicDEXDisconnected, dc.acct.host) - c.notify(newConnEventNote(TopicDEXDisconnected, subject, dc.acct.host, comms.Disconnected, details, db.ErrorLevel)) - continue - } + // Pending bonds will be handled by authDEX. Expired bonds will be + // refunded by rotateBonds. - wg.Add(1) - go func(dc *dexConnection) { - defer wg.Done() - err := c.authDEX(dc) - if err != nil { - subject, details := c.formatDetails(TopicDexAuthError, dc.acct.host, err) - c.notify(newDEXAuthNote(TopicDexAuthError, subject, dc.acct.host, false, details, db.ErrorLevel)) - return - } - }(dc) + // If the connection is down, authDEX will fail on Send. + if dc.IsDown() { + c.log.Warnf("Connection to %v not available for authorization. "+ + "It will automatically authorize when it connects.", dc.acct.host) + subject, details := c.formatDetails(TopicDEXDisconnected, dc.acct.host) + c.notify(newConnEventNote(TopicDEXDisconnected, subject, dc.acct.host, comms.Disconnected, details, db.ErrorLevel)) + return } - wg.Wait() + // Authenticate dex connection + err = c.authDEX(dc) + if err != nil { + subject, details := c.formatDetails(TopicDexAuthError, dc.acct.host, err) + c.notify(newDEXAuthNote(TopicDexAuthError, subject, dc.acct.host, false, details, db.ErrorLevel)) + } } // resolveActiveTrades loads order and match data from the database. Only active diff --git a/client/webserver/site/src/js/dexsettings.ts b/client/webserver/site/src/js/dexsettings.ts index 5936441d8f..43f4f226cc 100644 --- a/client/webserver/site/src/js/dexsettings.ts +++ b/client/webserver/site/src/js/dexsettings.ts @@ -176,7 +176,7 @@ export default class DexSettingsPage extends BasePage { }) this.dexAddrForm = new forms.DEXAddressForm(page.dexAddrForm, async (xc: Exchange) => { - window.location.assign(`/dexsettings/${xc.host}`) + app().loadPage(`/dexsettings/${xc.host}`) }, this.host) // forms.bind(page.bondDetailsForm, page.updateBondOptionsConfirm, () => this.updateBondOptions()) @@ -352,11 +352,10 @@ export default class DexSettingsPage extends BasePage { if (disable) { this.page.toggleAccountStatusBtn.textContent = intl.prep(intl.ID_ENABLE_ACCOUNT) Doc.hide(page.forms) - } else { - this.page.toggleAccountStatusBtn.textContent = intl.prep(intl.ID_DISABLE_ACCOUNT) - } + } else this.page.toggleAccountStatusBtn.textContent = intl.prep(intl.ID_DISABLE_ACCOUNT) + this.accountDisabled = disable - window.location.assign(`/dexsettings/${host}`) + app().loadPage(`dexsettings/${host}`) } async prepareAccountDisable (disableAccountForm: HTMLElement) { diff --git a/client/webserver/site/src/js/forms.ts b/client/webserver/site/src/js/forms.ts index b7e801eb3b..f02c6f2ca4 100644 --- a/client/webserver/site/src/js/forms.ts +++ b/client/webserver/site/src/js/forms.ts @@ -1037,17 +1037,13 @@ export class FeeAssetSelectionForm { this.marketRows.push({ mkt, tmpl, setTier }) } - if (xc.assets) { - for (const { symbol, id: assetID } of Object.values(xc.assets)) { - if (!app().assets[assetID]) continue - const bondAsset = xc.bondAssets[symbol] - if (bondAsset) addBondRow(assetID, bondAsset) - } + for (const { symbol, id: assetID } of Object.values(xc.assets || {})) { + if (!app().assets[assetID]) continue + const bondAsset = xc.bondAssets[symbol] + if (bondAsset) addBondRow(assetID, bondAsset) } - if (xc.markets) { - for (const mkt of Object.values(xc.markets)) addMarketRow(mkt) - } + for (const mkt of Object.values(xc.markets || {})) addMarketRow(mkt) // page.host.textContent = xc.host page.tradingTierInput.value = xc.auth.targetTier ? String(xc.auth.targetTier) : '1'