Skip to content

Commit

Permalink
multi: allow enabling and disabling of dex account
Browse files Browse the repository at this point in the history
Signed-off-by: Philemon Ukane <[email protected]>
  • Loading branch information
ukane-philemon committed Sep 4, 2024
1 parent 8c23904 commit 11883cb
Show file tree
Hide file tree
Showing 14 changed files with 136 additions and 54 deletions.
38 changes: 24 additions & 14 deletions client/core/account.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ import (
"github.com/decred/dcrd/dcrec/secp256k1/v4"
)

// disconnectDEX unsubscribes from the dex's orderbooks, ends the connection
// with the dex, and removes it from the connection map.
func (c *Core) disconnectDEX(dc *dexConnection) {
// stopDEXConnection unsubscribes from the dex's orderbooks and ends the
// connection with the dex. The dexConnection will still remain in c.conns map.
func (c *Core) stopDEXConnection(dc *dexConnection) {
// Stop dexConnection books.
dc.cfgMtx.RLock()
if dc.cfg != nil {
Expand All @@ -34,8 +34,13 @@ func (c *Core) disconnectDEX(dc *dexConnection) {
}
}
dc.cfgMtx.RUnlock()
dc.connMaster.Disconnect() // disconnect
}

// disconnectDEX disconnects a dex and removes it from the connection map.
func (c *Core) disconnectDEX(dc *dexConnection) {
// Disconnect and delete connection from map.
dc.connMaster.Disconnect()
c.stopDEXConnection(dc)
c.connMtx.Lock()
delete(c.conns, dc.acct.host)
c.connMtx.Unlock()
Expand All @@ -45,19 +50,19 @@ func (c *Core) disconnectDEX(dc *dexConnection) {
// application password.
func (c *Core) ToggleAccountStatus(pw []byte, addr string, disable bool) error {
// Validate password.
_, err := c.encryptionKey(pw)
crypter, err := c.encryptionKey(pw)
if err != nil {
return codedError(passwordErr, err)
}

var dc *dexConnection
if disable {
// Get dex connection by host.
dc, _, err = c.dex(addr)
if err != nil {
return newError(unknownDEXErr, "error retrieving dex conn: %w", err)
}
// Get dex connection by host. All exchange servers (enabled or not) are loaded as
// dexConnections but disabled servers are not connected.
dc, _, err := c.dex(addr)
if err != nil {
return newError(unknownDEXErr, "error retrieving dex conn: %w", err)
}

if disable {
// Check active orders or bonds.
if dc.hasActiveOrders() {
return fmt.Errorf("cannot disable account with active orders")
Expand All @@ -70,14 +75,19 @@ func (c *Core) ToggleAccountStatus(pw []byte, addr string, disable bool) error {
}

if disable {
c.disconnectDEX(dc)
dc.acct.toggleAccountStatus(true)
c.stopDEXConnection(dc)
} else {
acct, err := c.db.Account(addr)
if err != nil {
return err
}

c.connectAccount(acct)
if !c.connectAccount(acct) {
c.log.Errorf("Failed to establish connection to %s (will retry)", addr)
}

c.initializeDEXConnections(crypter)
}

return nil
Expand Down
8 changes: 6 additions & 2 deletions client/core/bond.go
Original file line number Diff line number Diff line change
Expand Up @@ -713,8 +713,6 @@ func (c *Core) rotateBonds(ctx context.Context) {
}
acctBondState := c.bondStateOfDEX(dc, bondCfg)

c.repostPendingBonds(dc, bondCfg, acctBondState, unlocked)

refundedAssets, expiredStrength, err := c.refundExpiredBonds(ctx, dc.acct, bondCfg, acctBondState, now)
if err != nil {
c.log.Errorf("Failed to refund expired bonds for %v: %v", dc.acct.host, err)
Expand All @@ -724,6 +722,12 @@ func (c *Core) rotateBonds(ctx context.Context) {
c.updateAssetBalance(assetID)
}

if dc.acct.isDisabled() {
continue // we can only attempt bond refund(if any) for disabled accounts
}

c.repostPendingBonds(dc, bondCfg, acctBondState, unlocked)

bondAsset := bondCfg.bondAssets[acctBondState.BondAssetID]
if bondAsset == nil {
if acctBondState.TargetTier > 0 {
Expand Down
23 changes: 15 additions & 8 deletions client/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,7 @@ func (c *Core) exchangeInfo(dc *dexConnection) *Exchange {
Host: dc.acct.host,
AcctID: acctID,
ConnectionStatus: dc.status(),
Disabled: dc.acct.isDisabled(),
}
}

Expand Down Expand Up @@ -493,6 +494,7 @@ func (c *Core) exchangeInfo(dc *dexConnection) *Exchange {
Auth: acctBondState.ExchangeAuth,
MaxScore: cfg.MaxScore,
PenaltyThreshold: cfg.PenaltyThreshold,
Disabled: dc.acct.isDisabled(),
}
}

Expand Down Expand Up @@ -5138,6 +5140,10 @@ func (c *Core) initializeDEXConnections(crypter encrypt.Crypter) {
continue
}

if dc.acct.isDisabled() {
continue // we can only unlock the dex account to init 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",
Expand Down Expand Up @@ -7113,12 +7119,6 @@ func (c *Core) initialize() error {
var liveConns uint32
var wg sync.WaitGroup
for _, acct := range accts {
if !acct.Active {
// TODO: We should list this account separatly for display on the
// dex settings page to allow re-enabling this server. But we should
// listen for unspent bond refund if any.
continue
}
wg.Add(1)
go func(acct *db.AccountInfo) {
defer wg.Done()
Expand Down Expand Up @@ -7172,7 +7172,8 @@ func (c *Core) initialize() error {
// connectAccount makes a connection to the DEX for the given account. If a
// non-nil dexConnection is returned from newDEXConnection, it was inserted into
// the conns map even if the connection attempt failed (connected == false), and
// the connect retry / keepalive loop is active.
// the connect retry / keepalive loop is active. The intial connection attempt
// or keepalive loop will not run if acct is disabled.
func (c *Core) connectAccount(acct *db.AccountInfo) (connected bool) {
host, err := addrHost(acct.Host)
if err != nil {
Expand Down Expand Up @@ -8156,7 +8157,7 @@ func (c *Core) startDexConnection(acctInfo *db.AccountInfo, dc *dexConnection) e
// the dexConnection's ConnectionMaster is shut down. This goroutine should
// be started as long as the reconnect loop is running. It only returns when
// the wsConn is stopped.
listen := dc.broadcastingConnect()
listen := dc.broadcastingConnect() && !dc.acct.isDisabled()
if listen {
c.wg.Add(1)
go c.listen(dc)
Expand Down Expand Up @@ -8203,6 +8204,12 @@ func (c *Core) startDexConnection(acctInfo *db.AccountInfo, dc *dexConnection) e
// according to ConnectResult.Bonds slice.
}

if dc.acct.isDisabled() {
// Sort out the bonds with current time to indicate refundable bonds.
categorizeBonds(time.Now().Unix())
return nil // nothing else to do
}

err := dc.connMaster.Connect(c.ctx)
if err != nil {
// Sort out the bonds with current time to indicate refundable bonds.
Expand Down
15 changes: 15 additions & 0 deletions client/core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ type Exchange struct {
Auth ExchangeAuth `json:"auth"`
PenaltyThreshold uint32 `json:"penaltyThreshold"`
MaxScore uint32 `json:"maxScore"`
Disabled bool `json:"disabled"`
}

// newDisplayIDFromSymbols creates a display-friendly market ID for a base/quote
Expand Down Expand Up @@ -817,6 +818,7 @@ type dexAccount struct {

authMtx sync.RWMutex
isAuthed bool
disabled bool
pendingBondsConfs map[string]uint32
pendingBonds []*db.Bond // not yet confirmed
bonds []*db.Bond // confirmed, and not yet expired
Expand All @@ -835,6 +837,7 @@ func newDEXAccount(acctInfo *db.AccountInfo, viewOnly bool) *dexAccount {
cert: acctInfo.Cert,
dexPubKey: acctInfo.DEXPubKey,
viewOnly: viewOnly,
disabled: acctInfo.Disabled,
encKey: acctInfo.EncKey(), // privKey and id on decrypt
pendingBondsConfs: make(map[string]uint32),
// bonds are set separately when categorized in connectDEX
Expand Down Expand Up @@ -958,6 +961,18 @@ func (a *dexAccount) status() (initialized, unlocked bool) {
return len(a.encKey) > 0, a.privKey != nil
}

func (a *dexAccount) isDisabled() bool {
a.authMtx.RLock()
defer a.authMtx.RUnlock()
return a.disabled
}

func (a *dexAccount) toggleAccountStatus(disable bool) {
a.authMtx.Lock()
defer a.authMtx.Unlock()
a.disabled = disable
}

// locked will be true if the account private key is currently decrypted, or
// there are no account keys generated yet.
func (a *dexAccount) locked() bool {
Expand Down
2 changes: 1 addition & 1 deletion client/db/bolt/db.go
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ func loadAccountInfo(acct *bbolt.Bucket, log dex.Logger) (*db.AccountInfo, error
return nil, err
}

acctInfo.Active = bytes.Equal(acct.Get(activeKey), byteTrue)
acctInfo.Disabled = bytes.Equal(acct.Get(activeKey), byteFalse)

bondsBkt := acct.Bucket(bondsSubBucket)
if bondsBkt == nil {
Expand Down
4 changes: 2 additions & 2 deletions client/db/bolt/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,7 +296,7 @@ func TestToggleAccountStatus(t *testing.T) {
t.Fatalf("Unexpected boltdb.Account error: %v", err)
}

if actualAcct.Active {
if !actualAcct.Disabled {
t.Fatalf("Expected a disabled account.")
}

Expand All @@ -311,7 +311,7 @@ func TestToggleAccountStatus(t *testing.T) {
t.Fatalf("Unexpected boltdb.Account error: %v", err)
}

if !actualAcct.Active {
if actualAcct.Disabled {
t.Fatalf("Expected an active account.")
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/db/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ type AccountInfo struct {
MaxBondedAmt uint64
PenaltyComps uint16
BondAsset uint32 // the asset to use when auto-posting bonds
Active bool // whether the account is enabled
Disabled bool // whether the account is disabled

// DEPRECATED reg fee data. Bond txns are in a sub-bucket.
// Left until we need to upgrade just for serialization simplicity.
Expand Down
8 changes: 8 additions & 0 deletions client/webserver/jsintl.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@ const (
archivedSettingsID = "ARCHIVED_SETTINGS"
idTransparent = "TRANSPARENT"
idNoCodeProvided = "NO_CODE_PROVIDED"
enableAccount = "ENABLE_ACCOUNT"
disableAccount = "DISABLE_ACCOUNT"
accountDisabledMsg = "ACCOUNT_DISABLED_MSG"
dexDisabledMsg = "DEX_DISABLED_MSG"
)

var enUS = map[string]*intl.Translation{
Expand Down Expand Up @@ -389,6 +393,10 @@ var enUS = map[string]*intl.Translation{
archivedSettingsID: {T: "Archived Settings"},
idTransparent: {T: "Transparent"},
idNoCodeProvided: {T: "no code provided"},
enableAccount: {T: "Enable Account"},
disableAccount: {T: "Disable Account"},
accountDisabledMsg: {T: "account disabled - re-enable to update settings"},
dexDisabledMsg: {T: "DEX server is disabled. Visit the settings page to enable and connect to this server."},
}

var ptBR = map[string]*intl.Translation{
Expand Down
3 changes: 2 additions & 1 deletion client/webserver/locales/en-us.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ var EnUS = map[string]*intl.Translation{
"Authorize Export": {T: "Authorize Export"},
"export_app_pw_msg": {T: "Enter your app password to confirm account export for"},
"Disable Account": {T: "Disable Account"},
"disable_dex_server": {T: "This DEX server may be re-enabled at any time in the future by adding it again."},
"disable_dex_server": {T: "This DEX server may be re-enabled at any time in the future on the settings page.", Version: 1},
"Authorize Import": {T: "Authorize Import"},
"app_pw_import_msg": {T: "Enter your app password to confirm account import"},
"Account File": {T: "Account File"},
Expand Down Expand Up @@ -653,4 +653,5 @@ var EnUS = map[string]*intl.Translation{
"Transaction": {T: "Transaction"},
"Value": {T: "Value"},
"Prepaid bond redeemed": {T: "Prepaid bond redeemed!"},
"Enable Account": {T: "Enable Account"},
}
32 changes: 20 additions & 12 deletions client/webserver/site/src/html/dexsettings.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{{define "dexsettings"}}
{{template "top" .}}
<div id="main" data-handler="dexsettings" data-host="{{.Exchange.Host}}" class="py-5 overflow-y-auto">
<div id="main" data-handler="dexsettings" data-host="{{.Exchange.Host}}" data-disabled="{{.Exchange.Disabled}}" class="py-5 overflow-y-auto">
<section class="flex-stretch-column mw-425 mx-auto pb-3 pt-2">
<div class="d-flex justify-content-start align-items-center">
<span id="goBackToSettings" class="ico-wide-headed-left-arrow fs24 py-1 px-2 lh1 hoverbg pointer"></span>
Expand All @@ -21,11 +21,13 @@
<div class="flex-center flex-grow-1 pe-3">
<div class="flex-stretch-column w-100">
<div class="d-flex justify-content-between align-items-center">
<span>[[[target_tier]]] <span class="fs14 ico-info me-1" data-tooltip="[[[target_tier_tooltip]]]"></span></span>
<span>[[[target_tier]]] <span class="fs14 ico-info me-1"
data-tooltip="[[[target_tier_tooltip]]]"></span></span>
<span id="targetTier"></span>
</div>
<div class="d-flex justify-content-between align-items-center">
<span>[[[Actual Tier]]] <span class="fs14 ico-info me-1" data-tooltip="[[[current_tier_tooltip]]]"></span></span>
<span>[[[Actual Tier]]] <span class="fs14 ico-info me-1"
data-tooltip="[[[current_tier_tooltip]]]"></span></span>
<span id="effectiveTier"></span>
</div>
<div class="d-flex justify-content-between align-items-center">
Expand All @@ -39,15 +41,15 @@
</div>
</div>
<div class="flex-center p-4 border-start">
<button id="changeTier">[[[Change Tier]]]</button>
<button id="changeTier" {{if .Exchange.Disabled}}disabled{{end}}>[[[Change Tier]]]</button>
</div>
</div>
<div id="otherBondSettings" class="d-flex align-items-stretch mt-3 border-top">
<div class="col-12 pe-2 py-2">
<div id="autoRenewBox" class="d-flex justify-content-between align-items-center hoverbg pointer">
<span>Auto Renew</span>
<div>
<div id="toggleAutoRenew" class="anitoggle"></div>
<div id="toggleAutoRenew" class="anitoggle" {{if .Exchange.Disabled}}disabled{{end}}></div>
</div>
</div>
<div id="renewErr" class="d-hide flex-center text-danger fs15"></div>
Expand All @@ -61,7 +63,7 @@
</div>
<div class="col-6 text-end">
<span id="penaltyComps"></span>
<input id="penaltyCompInput" type="number" step="1" class="micro thin text-center d-hide fs14">
<input id="penaltyCompInput" type="number" step="1" class="micro thin text-center d-hide fs14" {{if .Exchange.Disabled}}disabled{{end}}>
</div>
</div>
<div id="penaltyCompsErr" class="d-hide flex-center text-danger fs15"></div>
Expand All @@ -73,18 +75,24 @@
</div>
<div class="fs15 text-center d-hide text-danger text-break pt-3 mt-3 px-3 border-top" id="errMsg"></div>
<div class="settings mt-3 border-top">
<div class="border-bottom px-3 py-2">
<button id="toggleAccountStatusBtn">
{{if not .Exchange.Disabled}}
[[[Disable Account]]]
{{else}}
[[[Enable Account]]]
{{end}}
</button>
</div>
<div class="border-bottom px-3 py-2 {{if .Exchange.ViewOnly}}d-hide{{end}}">
<button id="exportDexBtn">[[[Export Account]]]</button>
</div>
<div class="border-bottom px-3 py-2">
<button id="disableAcctBtn">[[[Disable Account]]]</button>
</div>
<div class="border-bottom px-3 py-2">
<div class="border-bottom px-3 py-2 {{if .Exchange.Disabled}}d-hide{{end}}">
<input type="file" class="d-none" id="certFileInput">
<button id="updateCertBtn">[[[Update TLS Certificate]]]</button>
<span class="mx-2 d-hide text-success" id="updateCertMsg">[[[successful_cert_update]]]</span>
</div>
<div class="border-bottom px-3 py-2 border-bottom">
<div class="border-bottom px-3 py-2 border-bottom {{if .Exchange.Disabled}}d-hide{{end}}">
<button id="updateHostBtn">[[[update dex host]]]</button>
</div>
</div>
Expand Down Expand Up @@ -144,4 +152,4 @@

</div>
{{template "bottom"}}
{{end}}
{{end}}
Loading

0 comments on commit 11883cb

Please sign in to comment.