Skip to content

Commit 2370081

Browse files
authored
client: Fix login redirect loop. (#3571)
* client: Show link instead for disabled DEX. When a DEX account is disabled, the trade page now shows the DEX disabled message with a link to the settings page instead of the generic "Connection to server failed / Waiting to reconnect" overlay. * client: Skip token wallets with disabled parent. Token wallets (e.g. usdc.eth, polygon.eth) were not themselves marked disabled when only their parent chain wallet was disabled, causing error log spam on startup when the parent connection was refused. * client: Skip wallet lock when mixing. Avoids error log during shutdown. * client: Fix login redirect loop. When loadPage fetches a page but receives the login page instead (due to a server-side auth redirect), fall back to a full browser navigation rather than silently inserting the login page HTML. The browser's navigation handles cookie state more reliably than fetch. * server/db: Fix v8 upgrade for token market names. The v8 DB upgrade used raw market names from the DB (e.g. dcr_usdt.eth) which contain dots from token symbols. The actual PostgreSQL schema names replace dots with TKN via marketSchema(). Apply that conversion before constructing the table name for the ALTER TABLE statement. * client: Hide disabled networks from address form. Disabled wallets were shown in the network selector when requesting a receive address, leading to a broken address screen. Filter them out so only enabled wallets are selectable. * docs: Update release notes. Correct the server upgrade guidance: all in-flight swaps from v1.0.x are revoked on upgrade due to the per-match address requirement, so operators must wait for active swaps to complete. The previous advice about evm-protocol-overrides.json is insufficient on its own. Add known issues section noting that Electrum wallets are temporarily disabled due to inconsistencies found during testing, with a fix expected in a patch release. * client: Fix DCR explorer links returning 403. The dcrdata /tx/{txid}/out/{vout} path now returns 403. Link to the transaction page instead, consistent with other UTXO assets. * client: Fix multi-line locale keys in forms. Three locale keys were split across lines in the template, preventing the [[[...]]] replacement from matching: "pick a different asset", "Authorize Import", and "Wallet Balances". * dcr: Include mixed account funds in balance. When mixing is disabled but the mixed or trading accounts exist from a previous mixing session, their balances were invisible. Now those funds are reported as locked so the user can see they still exist. Ticket values in those accounts are also included in the staked category.
1 parent 8babd1b commit 2370081

File tree

11 files changed

+113
-46
lines changed

11 files changed

+113
-46
lines changed

client/asset/dcr/dcr.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1243,6 +1243,25 @@ func (dcr *ExchangeWallet) balance() (*asset.Balance, error) {
12431243
}
12441244

12451245
if accts.UnmixedAccount == "" {
1246+
// Mixing is disabled, but if the mixed or trading accounts exist
1247+
// and have funds (e.g. from a previous mixing session), include
1248+
// them as locked so the user can see they exist.
1249+
for _, acctName := range []string{mixedAccountName, tradingAccountName} {
1250+
acctBal, err := dcr.wallet.AccountBalance(dcr.ctx, 0, acctName)
1251+
if err != nil {
1252+
continue // account may not exist
1253+
}
1254+
total := toAtoms(acctBal.Total)
1255+
if total > 0 {
1256+
bal.Locked += total
1257+
}
1258+
staked := toAtoms(acctBal.LockedByTickets)
1259+
if staked > 0 {
1260+
bal.Other[asset.BalanceCategoryStaked] = asset.CustomBalance{
1261+
Amount: bal.Other[asset.BalanceCategoryStaked].Amount + staked,
1262+
}
1263+
}
1264+
}
12461265
return bal, nil
12471266
}
12481267

client/core/core.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1998,6 +1998,13 @@ fetchers:
19981998
continue
19991999
}
20002000
if !c.cfg.NoAutoWalletLock && wallet.unlocked() { // no-op if Logout did it
2001+
if mw, is := wallet.Wallet.(asset.FundsMixer); is {
2002+
if stats, err := mw.FundsMixingStats(); err == nil && stats.Enabled {
2003+
c.log.Infof("Skipping lock for %s wallet (mixing active)", strings.ToUpper(unbip(assetID)))
2004+
wallet.Disconnect()
2005+
continue
2006+
}
2007+
}
20012008
symb := strings.ToUpper(unbip(assetID))
20022009
c.log.Infof("Locking %s wallet", symb)
20032010
if err := wallet.Lock(walletLockTimeout); err != nil {
@@ -4951,6 +4958,12 @@ func (c *Core) connectWallets(crypter encrypt.Crypter) {
49514958
if wallet.isDisabled() {
49524959
return
49534960
}
4961+
// Return early if this is a token and the parent wallet is disabled.
4962+
if token := asset.TokenInfo(wallet.AssetID); token != nil {
4963+
if parentWallet, found := c.wallet(token.ParentID); found && parentWallet.isDisabled() {
4964+
return
4965+
}
4966+
}
49544967
if !wallet.connected() {
49554968
err := wallet.OpenWithPW(c.ctx, crypter)
49564969
if err != nil {
@@ -5163,6 +5176,12 @@ func (c *Core) Logout() error {
51635176
c.wg.Add(1)
51645177
for _, w := range c.xcWallets() {
51655178
if w.connected() && w.unlocked() {
5179+
if mw, is := w.Wallet.(asset.FundsMixer); is {
5180+
if stats, err := mw.FundsMixingStats(); err == nil && stats.Enabled {
5181+
c.log.Infof("Skipping lock for %s wallet (mixing active)", strings.ToUpper(unbip(w.AssetID)))
5182+
continue
5183+
}
5184+
}
51665185
symb := strings.ToUpper(unbip(w.AssetID))
51675186
c.log.Infof("Locking %s wallet", symb)
51685187
if err := w.Lock(walletLockTimeout); err != nil {

client/webserver/locales/en-us.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ var EnUS = map[string]*intl.Translation{
6868
"volume_24": {T: "24 Hr. Volume"},
6969
"Connection to server failed": {T: "Connection to server failed"},
7070
"Waiting to reconnect...": {T: "Waiting to reconnect..."},
71+
"Visit settings to re-enable": {T: "Visit settings to re-enable"},
7172
"High": {T: "High"},
7273
"Low": {T: "Low"},
7374
"buys": {T: "buys"},

client/webserver/site/src/html/forms.tmpl

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -482,8 +482,7 @@
482482
<div data-tmpl="text" class="fs16 reg-conf-msg mt-3 border-top">
483483
<span class="ico-info"></span> [[[reg_confirm_submit]]]
484484
</div>
485-
<div class="hoverbg pointer grey pt-2" data-tmpl="goBack"><span class="ico-arrowleft fs12 me-1"></span> [[[pick a
486-
different asset]]]</div>
485+
<div class="hoverbg pointer grey pt-2" data-tmpl="goBack"><span class="ico-arrowleft fs12 me-1"></span> [[[pick a different asset]]]</div>
487486
{{end}}
488487

489488
{{define "authorizeAccountImportForm"}}
@@ -501,8 +500,7 @@
501500
</small>
502501
</div>
503502
<div class="flex-stretch-column">
504-
<button id="authorizeImportAccountConfirm" type="button" class="mt-2 mx-3 mb-3 feature">[[[Authorize
505-
Import]]]</button>
503+
<button id="authorizeImportAccountConfirm" type="button" class="mt-2 mx-3 mb-3 feature">[[[Authorize Import]]]</button>
506504
</div>
507505
<div class="fs15 text-center d-hide text-danger text-break" id="importAccountErr"></div>
508506
{{end}}
@@ -1079,8 +1077,7 @@
10791077
<header id="orderReportTitle"></header>
10801078
<div id="orderReportError" class="text-danger d-flex flex-column"></div>
10811079
<div id="orderReportDetails" class="d-flex flex-column">
1082-
<div class="fs18 mt-3 align-self-start"><img class="logo-square small"><span class="ms-2">[[[Wallet
1083-
Balances]]]</span></div>
1080+
<div class="fs18 mt-3 align-self-start"><img class="logo-square small"><span class="ms-2">[[[Wallet Balances]]]</span></div>
10841081
<table class="table striped border mt-3 w-auto mx-auto">
10851082
<thead>
10861083
<tr>

client/webserver/site/src/html/markets.tmpl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,9 +99,16 @@
9999

100100
<div class="flex-grow-1 position-relative">
101101
<div id="disconnectedOverlay" class="d-hide fill-abs flex-center flex-column" style="z-index:10; background-color: rgba(0,0,0,0.5);">
102-
<span class="ico-disconnected fs40 text-danger mb-3"></span>
103-
<span class="fs22 demi">[[[Connection to server failed]]]</span>
104-
<span class="fs16 grey mt-2">[[[Waiting to reconnect...]]]</span>
102+
<div id="disconnectedContent" class="flex-center flex-column">
103+
<span class="ico-disconnected fs40 text-danger mb-3"></span>
104+
<span class="fs22 demi">[[[Connection to server failed]]]</span>
105+
<span class="fs16 grey mt-2">[[[Waiting to reconnect...]]]</span>
106+
</div>
107+
<div id="disabledContent" class="flex-center flex-column d-hide">
108+
<span class="ico-settings fs40 grey mb-3"></span>
109+
<span id="disabledMsg" class="fs22 demi"></span>
110+
<a id="disabledSettingsLink" class="fs16 pointer mt-3 underline">[[[Visit settings to re-enable]]]</a>
111+
</div>
105112
</div>
106113
<div id="mainContent">
107114

client/webserver/site/src/js/app.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -336,9 +336,13 @@ export default class Application {
336336
* reconnected is called by the websocket client when a reconnection is made.
337337
*/
338338
reconnected () {
339+
// Skip reload if the user is on the login page and a login is likely
340+
// in progress. The long login (connecting wallets) can outlast the
341+
// websocket ping timeout, causing a reconnect that would abort the
342+
// pending login POST and lose the auth cookie.
343+
if (this.main?.dataset.handler === 'login') return
339344
if (this.main?.dataset.handler === 'settings') window.location.assign('/')
340-
else window.location.reload() // This triggers another websocket disconnect/connect (!)
341-
// a fetchUser() and loadPage(window.history.state.page) might work
345+
else window.location.reload()
342346
}
343347

344348
/*
@@ -404,6 +408,12 @@ export default class Application {
404408
const doc = Doc.noderize(html)
405409
const main = idel(doc, 'main')
406410
const delivered = main.dataset.handler
411+
// If we were redirected to the login page, fall back to a full page
412+
// navigation which is more reliable for cookie handling.
413+
if (delivered === 'login' && requestedHandler !== 'login') {
414+
window.location.assign(url.toString())
415+
return false
416+
}
407417
// Append the request to the page history.
408418
if (!skipPush) {
409419
const path = delivered === requestedHandler ? url.toString() : `/${delivered}`

client/webserver/site/src/js/coinexplorers.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -66,21 +66,9 @@ const optimismExplorers: Record<number, (cid: string) => string> = {
6666

6767
export const CoinExplorers: Record<number, Record<number, (cid: string) => string>> = {
6868
42: { // dcr
69-
[Mainnet]: (cid: string) => {
70-
const [txid, vout] = cid.split(':')
71-
if (vout !== undefined) return `https://dcrdata.decred.org/tx/${txid}/out/${vout}`
72-
return `https://dcrdata.decred.org/tx/${txid}`
73-
},
74-
[Testnet]: (cid: string) => {
75-
const [txid, vout] = cid.split(':')
76-
if (vout !== undefined) return `https://testnet.decred.org/tx/${txid}/out/${vout}`
77-
return `https://testnet.decred.org/tx/${txid}`
78-
},
79-
[Simnet]: (cid: string) => {
80-
const [txid, vout] = cid.split(':')
81-
if (vout !== undefined) return `http://127.0.0.1:17779/tx/${txid}/out/${vout}`
82-
return `https://127.0.0.1:17779/tx/${txid}`
83-
}
69+
[Mainnet]: (cid: string) => `https://dcrdata.decred.org/tx/${cid.split(':')[0]}`,
70+
[Testnet]: (cid: string) => `https://testnet.decred.org/tx/${cid.split(':')[0]}`,
71+
[Simnet]: (cid: string) => `http://127.0.0.1:17779/tx/${cid.split(':')[0]}`
8472
},
8573
0: { // btc
8674
[Mainnet]: (cid: string) => `https://mempool.space/tx/${cid.split(':')[0]}`,

client/webserver/site/src/js/markets.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1132,10 +1132,17 @@ export default class MarketsPage extends BasePage {
11321132
// exchange data, so just put up a message and wait for the connection to be
11331133
// established, at which time handleConnNote will refresh and reload.
11341134
if (!dex || !dex.markets || dex.connectionStatus !== ConnectionStatus.Connected) {
1135-
let errMsg = intl.prep(intl.ID_CONNECTION_FAILED)
1136-
if (dex.disabled) errMsg = intl.prep(intl.ID_DEX_DISABLED_MSG)
1137-
page.chartErrMsg.textContent = errMsg
1138-
Doc.show(page.chartErrMsg, page.disconnectedOverlay)
1135+
if (dex && dex.disabled) {
1136+
page.disabledMsg.textContent = intl.prep(intl.ID_DEX_DISABLED_MSG)
1137+
page.disabledSettingsLink.href = `/dexsettings/${dex.host}`
1138+
Doc.show(page.disabledContent)
1139+
Doc.hide(page.disconnectedContent)
1140+
} else {
1141+
page.chartErrMsg.textContent = intl.prep(intl.ID_CONNECTION_FAILED)
1142+
Doc.show(page.chartErrMsg, page.disconnectedContent)
1143+
Doc.hide(page.disabledContent)
1144+
}
1145+
Doc.show(page.disconnectedOverlay)
11391146
return
11401147
}
11411148

client/webserver/site/src/js/wallets.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2375,7 +2375,18 @@ export default class WalletsPage extends BasePage {
23752375
/* Display a deposit address. */
23762376
async showDeposit () {
23772377
const { page, selectedTicker: { networkAssets } } = this
2378-
const assetIDs = networkAssets.map(({ assetID }: NetworkAsset) => assetID)
2378+
const assetIDs = networkAssets
2379+
.filter(({ assetID, token }: NetworkAsset) => {
2380+
const w = app().walletMap[assetID]
2381+
if (!w || w.disabled) return false
2382+
if (token) {
2383+
const pw = app().walletMap[token.parentID]
2384+
if (pw && pw.disabled) return false
2385+
}
2386+
return true
2387+
})
2388+
.map(({ assetID }: NetworkAsset) => assetID)
2389+
if (assetIDs.length === 0) return
23792390
this.depositAddrForm.setAssetSelect(assetIDs)
23802391
this.forms.show(page.deposit)
23812392
}

docs/release-notes/release-notes-1.1.0.md

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,29 @@ This release introduces per-match swap addresses, which change the trade
1818
negotiation protocol. Old clients cannot trade on a v1.1.0 server and new
1919
clients cannot trade on a v1.0.x server.
2020

21-
### EVM Contract Upgrade (Server Operators)
21+
### Upgrading (Server Operators)
2222

23-
v1.0.6 used EVM contract version 0. v1.1.0 defaults to contract version 1.
24-
The server's RPC client binds to a single contract version, so after upgrading
25-
the server cannot verify coins from in-flight v0 swaps. To handle this safely,
26-
operators should either:
23+
All active swaps from v1.0.x will be revoked on upgrade because the v1.1.0
24+
server requires per-match swap addresses on all matches. Revoked matches are
25+
marked as no-fault, so neither party is penalized, but participants with
26+
on-chain swaps will need to wait for locktime expiry to refund.
2727

28-
1. Wait for all active EVM swaps to complete before upgrading, OR
29-
2. Place an `evm-protocol-overrides.json` file in the server's working
30-
directory to keep v0 until remaining swaps drain:
28+
**Operators must wait for all active swaps to complete before upgrading.**
3129

32-
```json
33-
{"eth": 0, "usdc.eth": 0, "usdt.eth": 0, "matic.eth": 0}
34-
```
30+
Additionally, v1.0.6 used EVM contract version 0 while v1.1.0 defaults to
31+
contract version 1. An `evm-protocol-overrides.json` file can be placed in
32+
the server's working directory to override the contract version for specific
33+
assets, but this does not prevent the per-match address revocation above.
3534

36-
Once no v0 swaps remain, remove the file and restart to use v1.
35+
## Known Issues
36+
37+
### Electrum Wallets Temporarily Disabled
38+
39+
Electrum wallet support (BTC, LTC, FIRO) has been temporarily disabled due to
40+
inconsistencies found during testing. Existing Electrum wallets will continue
41+
to function, but new Electrum wallet creation is not available in this release.
42+
Users should use native SPV or RPC wallets instead. A fix is expected in a
43+
subsequent patch release.
3744

3845
## New Features
3946

0 commit comments

Comments
 (0)