diff --git a/background/background.go b/background/background.go index 92886f0..821f95d 100644 --- a/background/background.go +++ b/background/background.go @@ -12,7 +12,6 @@ import ( "sync" "time" - "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/vspd/database" "github.com/decred/vspd/rpc" "github.com/jrick/wsrpc/v2" @@ -22,7 +21,6 @@ var ( db *database.VspDatabase dcrdRPC rpc.DcrdConnect walletRPC rpc.WalletConnect - netParams *chaincfg.Params notifierClosed chan struct{} ) @@ -76,7 +74,7 @@ func blockConnected() { ctx := context.Background() - dcrdClient, _, err := dcrdRPC.Client(ctx, netParams) + dcrdClient, _, err := dcrdRPC.Client(ctx) if err != nil { log.Errorf("%s: %v", funcName, err) return @@ -170,7 +168,7 @@ func blockConnected() { log.Errorf("%s: db.GetUnconfirmedFees error: %v", funcName, err) } - walletClients, failedConnections := walletRPC.Clients(ctx, netParams) + walletClients, failedConnections := walletRPC.Clients(ctx) if len(walletClients) == 0 { log.Errorf("%s: Could not connect to any wallets", funcName) return @@ -333,7 +331,7 @@ func blockConnected() { func connectNotifier(shutdownCtx context.Context, dcrdWithNotifs rpc.DcrdConnect) error { notifierClosed = make(chan struct{}) - dcrdClient, _, err := dcrdWithNotifs.Client(shutdownCtx, netParams) + dcrdClient, _, err := dcrdWithNotifs.Client(shutdownCtx) if err != nil { return err } @@ -361,12 +359,11 @@ func connectNotifier(shutdownCtx context.Context, dcrdWithNotifs rpc.DcrdConnect } func Start(shutdownCtx context.Context, wg *sync.WaitGroup, vdb *database.VspDatabase, drpc rpc.DcrdConnect, - dcrdWithNotif rpc.DcrdConnect, wrpc rpc.WalletConnect, p *chaincfg.Params) { + dcrdWithNotif rpc.DcrdConnect, wrpc rpc.WalletConnect) { db = vdb dcrdRPC = drpc walletRPC = wrpc - netParams = p // Run the block connected handler now to catch up with any blocks mined // while vspd was shut down. @@ -428,13 +425,13 @@ func checkWalletConsistency() { ctx := context.Background() - dcrdClient, _, err := dcrdRPC.Client(ctx, netParams) + dcrdClient, _, err := dcrdRPC.Client(ctx) if err != nil { log.Errorf("%s: %v", funcName, err) return } - walletClients, failedConnections := walletRPC.Clients(ctx, netParams) + walletClients, failedConnections := walletRPC.Clients(ctx) if len(walletClients) == 0 { log.Errorf("%s: Could not connect to any wallets", funcName) return diff --git a/database/database.go b/database/database.go index f319e2b..5a99172 100644 --- a/database/database.go +++ b/database/database.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2021 The Decred developers +// Copyright (c) 2020-2022 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -16,7 +16,6 @@ import ( "sync" "time" - "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/vspd/rpc" bolt "go.etcd.io/bbolt" ) @@ -386,7 +385,7 @@ func (vdb *VspDatabase) BackupDB(w http.ResponseWriter) error { // CheckIntegrity will ensure that all data in the database is present and up to // date. -func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, params *chaincfg.Params, dcrd rpc.DcrdConnect) error { +func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, dcrd rpc.DcrdConnect) error { // Ensure all confirmed tickets have a purchase height. // This is necessary because of an old bug which, in some circumstances, @@ -401,7 +400,7 @@ func (vdb *VspDatabase) CheckIntegrity(ctx context.Context, params *chaincfg.Par return nil } - dcrdClient, _, err := dcrd.Client(ctx, params) + dcrdClient, _, err := dcrd.Client(ctx) if err != nil { return err } diff --git a/rpc/dcrd.go b/rpc/dcrd.go index f966e2f..0ce3f96 100644 --- a/rpc/dcrd.go +++ b/rpc/dcrd.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 The Decred developers +// Copyright (c) 2020-2022 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -41,45 +41,53 @@ type DcrdRPC struct { } type DcrdConnect struct { - *client + client *client + params *chaincfg.Params } -func SetupDcrd(user, pass, addr string, cert []byte, n wsrpc.Notifier) DcrdConnect { - return DcrdConnect{setup(user, pass, addr, cert, n)} +func SetupDcrd(user, pass, addr string, cert []byte, n wsrpc.Notifier, params *chaincfg.Params) DcrdConnect { + return DcrdConnect{ + client: setup(user, pass, addr, cert, n), + params: params, + } +} + +func (d *DcrdConnect) Close() { + d.client.Close() } // Client creates a new DcrdRPC client instance. Returns an error if dialing // dcrd fails or if dcrd is misconfigured. -func (d *DcrdConnect) Client(ctx context.Context, netParams *chaincfg.Params) (*DcrdRPC, string, error) { - c, newConnection, err := d.dial(ctx) +func (d *DcrdConnect) Client(ctx context.Context) (*DcrdRPC, string, error) { + c, newConnection, err := d.client.dial(ctx) if err != nil { - return nil, d.addr, fmt.Errorf("dcrd connection error: %w", err) + return nil, d.client.addr, fmt.Errorf("dcrd connection error: %w", err) } // If this is a reused connection, we don't need to validate the dcrd config // again. if !newConnection { - return &DcrdRPC{c, ctx}, d.addr, nil + return &DcrdRPC{c, ctx}, d.client.addr, nil } // Verify dcrd is at the required api version. var verMap map[string]dcrdtypes.VersionResult err = c.Call(ctx, "version", &verMap) if err != nil { - d.Close() - return nil, d.addr, fmt.Errorf("dcrd version check failed: %w", err) + d.client.Close() + return nil, d.client.addr, fmt.Errorf("dcrd version check failed: %w", err) } ver, exists := verMap["dcrdjsonrpcapi"] if !exists { - d.Close() - return nil, d.addr, fmt.Errorf("dcrd version response missing 'dcrdjsonrpcapi'") + d.client.Close() + return nil, d.client.addr, fmt.Errorf("dcrd version response missing 'dcrdjsonrpcapi'") } sVer := semver{ver.Major, ver.Minor, ver.Patch} if !semverCompatible(requiredDcrdVersion, sVer) { - d.Close() - return nil, d.addr, fmt.Errorf("dcrd has incompatible JSON-RPC version: got %s, expected %s", + d.client.Close() + return nil, d.client.addr, fmt.Errorf("dcrd has incompatible JSON-RPC version: got %s, expected %s", sVer, requiredDcrdVersion) } @@ -87,27 +95,27 @@ func (d *DcrdConnect) Client(ctx context.Context, netParams *chaincfg.Params) (* var netID wire.CurrencyNet err = c.Call(ctx, "getcurrentnet", &netID) if err != nil { - d.Close() - return nil, d.addr, fmt.Errorf("dcrd getcurrentnet check failed: %w", err) + d.client.Close() + return nil, d.client.addr, fmt.Errorf("dcrd getcurrentnet check failed: %w", err) } - if netID != netParams.Net { - d.Close() - return nil, d.addr, fmt.Errorf("dcrd running on %s, expected %s", netID, netParams.Net) + if netID != d.params.Net { + d.client.Close() + return nil, d.client.addr, fmt.Errorf("dcrd running on %s, expected %s", netID, d.params.Net) } // Verify dcrd has tx index enabled (required for getrawtransaction). var info dcrdtypes.InfoChainResult err = c.Call(ctx, "getinfo", &info) if err != nil { - d.Close() - return nil, d.addr, fmt.Errorf("dcrd getinfo check failed: %w", err) + d.client.Close() + return nil, d.client.addr, fmt.Errorf("dcrd getinfo check failed: %w", err) } if !info.TxIndex { - d.Close() - return nil, d.addr, errors.New("dcrd does not have transaction index enabled (--txindex)") + d.client.Close() + return nil, d.client.addr, errors.New("dcrd does not have transaction index enabled (--txindex)") } - return &DcrdRPC{c, ctx}, d.addr, nil + return &DcrdRPC{c, ctx}, d.client.addr, nil } // GetRawTransaction uses getrawtransaction RPC to retrieve details about the diff --git a/rpc/dcrwallet.go b/rpc/dcrwallet.go index 08c91cd..3b95ad0 100644 --- a/rpc/dcrwallet.go +++ b/rpc/dcrwallet.go @@ -25,32 +25,38 @@ type WalletRPC struct { ctx context.Context } -type WalletConnect []*client +type WalletConnect struct { + clients []*client + params *chaincfg.Params +} -func SetupWallet(user, pass, addrs []string, cert [][]byte) WalletConnect { - walletConnect := make(WalletConnect, len(addrs)) +func SetupWallet(user, pass, addrs []string, cert [][]byte, params *chaincfg.Params) WalletConnect { + clients := make([]*client, len(addrs)) for i := 0; i < len(addrs); i++ { - walletConnect[i] = setup(user[i], pass[i], addrs[i], cert[i], nil) + clients[i] = setup(user[i], pass[i], addrs[i], cert[i], nil) } - return walletConnect + return WalletConnect{ + clients: clients, + params: params, + } } func (w *WalletConnect) Close() { - for _, connect := range []*client(*w) { - connect.Close() + for _, client := range w.clients { + client.Close() } } // Clients loops over each wallet and tries to establish a connection. It // increments a count of failed connections if a connection cannot be // established, or if the wallet is misconfigured. -func (w *WalletConnect) Clients(ctx context.Context, netParams *chaincfg.Params) ([]*WalletRPC, []string) { +func (w *WalletConnect) Clients(ctx context.Context) ([]*WalletRPC, []string) { walletClients := make([]*WalletRPC, 0) failedConnections := make([]string, 0) - for _, connect := range []*client(*w) { + for _, connect := range w.clients { c, newConnection, err := connect.dial(ctx) if err != nil { @@ -103,9 +109,9 @@ func (w *WalletConnect) Clients(ctx context.Context, netParams *chaincfg.Params) connect.Close() continue } - if netID != netParams.Net { + if netID != w.params.Net { log.Errorf("dcrwallet on wrong network (wallet=%s): running on %s, expected %s", - c.String(), netID, netParams.Net) + c.String(), netID, w.params.Net) failedConnections = append(failedConnections, connect.addr) connect.Close() continue diff --git a/vspd.go b/vspd.go index c4754b0..054e3f0 100644 --- a/vspd.go +++ b/vspd.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 The Decred developers +// Copyright (c) 2020-2022 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -84,15 +84,15 @@ func run(ctx context.Context) error { // Create RPC client for local dcrd instance (used for broadcasting and // checking the status of fee transactions). - dcrd := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass, cfg.DcrdHost, cfg.dcrdCert, nil) + dcrd := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass, cfg.DcrdHost, cfg.dcrdCert, nil, cfg.netParams.Params) defer dcrd.Close() // Create RPC client for remote dcrwallet instance (used for voting). - wallets := rpc.SetupWallet(cfg.walletUsers, cfg.walletPasswords, cfg.walletHosts, cfg.walletCerts) + wallets := rpc.SetupWallet(cfg.walletUsers, cfg.walletPasswords, cfg.walletHosts, cfg.walletCerts, cfg.netParams.Params) defer wallets.Close() // Ensure all data in database is present and up-to-date. - err = db.CheckIntegrity(ctx, cfg.netParams.Params, dcrd) + err = db.CheckIntegrity(ctx, dcrd) if err != nil { // vspd should still start if this fails, so just log an error. log.Errorf("Could not check database integrity: %v", err) @@ -124,12 +124,12 @@ func run(ctx context.Context) error { // Create a dcrd client with a blockconnected notification handler. notifHandler := background.NotificationHandler{ShutdownWg: &shutdownWg} dcrdWithNotifs := rpc.SetupDcrd(cfg.DcrdUser, cfg.DcrdPass, - cfg.DcrdHost, cfg.dcrdCert, ¬ifHandler) + cfg.DcrdHost, cfg.dcrdCert, ¬ifHandler, cfg.netParams.Params) defer dcrdWithNotifs.Close() // Start background process which will continually attempt to reconnect to // dcrd if the connection drops. - background.Start(ctx, &shutdownWg, db, dcrd, dcrdWithNotifs, wallets, cfg.netParams.Params) + background.Start(ctx, &shutdownWg, db, dcrd, dcrdWithNotifs, wallets) // Wait for shutdown tasks to complete before running deferred tasks and // returning. diff --git a/webapi/admin.go b/webapi/admin.go index 9ac9089..8559752 100644 --- a/webapi/admin.go +++ b/webapi/admin.go @@ -143,7 +143,7 @@ func (s *Server) statusJSON(c *gin.Context) { // adminPage is the handler for "GET /admin". func (s *Server) adminPage(c *gin.Context) { c.HTML(http.StatusOK, "admin.html", gin.H{ - "WebApiCache": getCache(), + "WebApiCache": s.cache.getData(), "WebApiCfg": s.cfg, "WalletStatus": walletStatus(c), "DcrdStatus": dcrdStatus(c), @@ -185,7 +185,7 @@ func (s *Server) ticketSearch(c *gin.Context) { VoteChanges: voteChanges, MaxVoteChanges: s.cfg.MaxVoteChangeRecords, }, - "WebApiCache": getCache(), + "WebApiCache": s.cache.getData(), "WebApiCfg": s.cfg, "WalletStatus": walletStatus(c), "DcrdStatus": dcrdStatus(c), @@ -200,7 +200,7 @@ func (s *Server) adminLogin(c *gin.Context) { if password != s.cfg.AdminPass { log.Warnf("Failed login attempt from %s", c.ClientIP()) c.HTML(http.StatusUnauthorized, "login.html", gin.H{ - "WebApiCache": getCache(), + "WebApiCache": s.cache.getData(), "WebApiCfg": s.cfg, "IncorrectPassword": true, }) diff --git a/webapi/cache.go b/webapi/cache.go index 3155459..e19adab 100644 --- a/webapi/cache.go +++ b/webapi/cache.go @@ -10,15 +10,21 @@ import ( "sync" "time" - "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/vspd/database" "github.com/decred/vspd/rpc" "github.com/dustin/go-humanize" ) -// apiCache is used to cache values which are commonly used by the API, so +// cache is used to store values which are commonly used by the API, so // repeated web requests don't repeatedly trigger DB or RPC calls. -type apiCache struct { +type cache struct { + // data is the cached data. + data cacheData + // mtx must be held to read/write cache data. + mtx sync.RWMutex +} + +type cacheData struct { UpdateTime string PubKey string DatabaseSize string @@ -32,31 +38,26 @@ type apiCache struct { RevokedProportion float32 } -var cacheMtx sync.RWMutex -var cache apiCache +func (c *cache) getData() cacheData { + c.mtx.RLock() + defer c.mtx.RUnlock() -func getCache() apiCache { - cacheMtx.RLock() - defer cacheMtx.RUnlock() - - return cache + return c.data } -// initCache creates the struct which holds the cached VSP stats, and -// initializes it with static values. -func initCache(signPubKey string) { - cacheMtx.Lock() - defer cacheMtx.Unlock() - - cache = apiCache{ - PubKey: signPubKey, +// newCache creates a new cache and initializes it with static values. +func newCache(signPubKey string) *cache { + return &cache{ + data: cacheData{ + PubKey: signPubKey, + }, } } -// updateCache updates the dynamic values in the cache (ticket counts and best -// block height). -func updateCache(ctx context.Context, db *database.VspDatabase, - dcrd rpc.DcrdConnect, netParams *chaincfg.Params, wallets rpc.WalletConnect) error { +// update will use the provided database and RPC connections to update the +// dynamic values in the cache. +func (c *cache) update(ctx context.Context, db *database.VspDatabase, + dcrd rpc.DcrdConnect, wallets rpc.WalletConnect) error { dbSize, err := db.Size() if err != nil { @@ -70,7 +71,7 @@ func updateCache(ctx context.Context, db *database.VspDatabase, } // Get latest best block height. - dcrdClient, _, err := dcrd.Client(ctx, netParams) + dcrdClient, _, err := dcrd.Client(ctx) if err != nil { return err } @@ -84,7 +85,7 @@ func updateCache(ctx context.Context, db *database.VspDatabase, return errors.New("dcr node reports a network ticket pool size of zero") } - clients, failedConnections := wallets.Clients(ctx, netParams) + clients, failedConnections := wallets.Clients(ctx) if len(clients) == 0 { log.Error("Could not connect to any wallets") } else if len(failedConnections) > 0 { @@ -92,29 +93,29 @@ func updateCache(ctx context.Context, db *database.VspDatabase, len(failedConnections), len(clients)) } - cacheMtx.Lock() - defer cacheMtx.Unlock() + c.mtx.Lock() + defer c.mtx.Unlock() - cache.UpdateTime = dateTime(time.Now().Unix()) - cache.DatabaseSize = humanize.Bytes(dbSize) - cache.Voting = voting - cache.Voted = voted - cache.TotalVotingWallets = int64(len(clients) + len(failedConnections)) - cache.VotingWalletsOnline = int64(len(clients)) - cache.Revoked = revoked - cache.BlockHeight = bestBlock.Height - cache.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize) + c.data.UpdateTime = dateTime(time.Now().Unix()) + c.data.DatabaseSize = humanize.Bytes(dbSize) + c.data.Voting = voting + c.data.Voted = voted + c.data.TotalVotingWallets = int64(len(clients) + len(failedConnections)) + c.data.VotingWalletsOnline = int64(len(clients)) + c.data.Revoked = revoked + c.data.BlockHeight = bestBlock.Height + c.data.NetworkProportion = float32(voting) / float32(bestBlock.PoolSize) // Prevent dividing by zero when pool has no voted tickets. switch voted { case 0: if revoked == 0 { - cache.RevokedProportion = 0 + c.data.RevokedProportion = 0 } else { - cache.RevokedProportion = 1 + c.data.RevokedProportion = 1 } default: - cache.RevokedProportion = float32(revoked) / float32(voted) + c.data.RevokedProportion = float32(revoked) / float32(voted) } return nil diff --git a/webapi/homepage.go b/webapi/homepage.go index 4f218f3..e2f71fd 100644 --- a/webapi/homepage.go +++ b/webapi/homepage.go @@ -12,7 +12,7 @@ import ( func (s *Server) homepage(c *gin.Context) { c.HTML(http.StatusOK, "homepage.html", gin.H{ - "WebApiCache": getCache(), + "WebApiCache": s.cache.getData(), "WebApiCfg": s.cfg, }) } diff --git a/webapi/middleware.go b/webapi/middleware.go index 6d0b45b..8bcf61b 100644 --- a/webapi/middleware.go +++ b/webapi/middleware.go @@ -59,7 +59,7 @@ func (s *Server) requireAdmin(c *gin.Context) { if admin == nil { c.HTML(http.StatusUnauthorized, "login.html", gin.H{ - "WebApiCache": getCache(), + "WebApiCache": s.cache.getData(), "WebApiCfg": s.cfg, }) c.Abort() @@ -69,9 +69,9 @@ func (s *Server) requireAdmin(c *gin.Context) { // withDcrdClient middleware adds a dcrd client to the request context for // downstream handlers to make use of. -func withDcrdClient(dcrd rpc.DcrdConnect, cfg Config) gin.HandlerFunc { +func withDcrdClient(dcrd rpc.DcrdConnect) gin.HandlerFunc { return func(c *gin.Context) { - client, hostname, err := dcrd.Client(c, cfg.NetParams) + client, hostname, err := dcrd.Client(c) // Don't handle the error here, add it to the context and let downstream // handlers decide what to do with it. c.Set(dcrdKey, client) @@ -83,9 +83,9 @@ func withDcrdClient(dcrd rpc.DcrdConnect, cfg Config) gin.HandlerFunc { // withWalletClients middleware attempts to add voting wallet clients to the // request context for downstream handlers to make use of. Downstream handlers // must handle the case where no wallet clients are connected. -func withWalletClients(wallets rpc.WalletConnect, cfg Config) gin.HandlerFunc { +func withWalletClients(wallets rpc.WalletConnect) gin.HandlerFunc { return func(c *gin.Context) { - clients, failedConnections := wallets.Clients(c, cfg.NetParams) + clients, failedConnections := wallets.Clients(c) if len(clients) == 0 { log.Error("Could not connect to any wallets") } else if len(failedConnections) > 0 { diff --git a/webapi/vspinfo.go b/webapi/vspinfo.go index 72e0560..c7aa262 100644 --- a/webapi/vspinfo.go +++ b/webapi/vspinfo.go @@ -13,7 +13,7 @@ import ( // vspInfo is the handler for "GET /api/v3/vspinfo". func (s *Server) vspInfo(c *gin.Context) { - cachedStats := getCache() + cachedStats := s.cache.getData() s.sendJSONResponse(vspInfoResponse{ APIVersions: []int64{3}, Timestamp: time.Now().Unix(), diff --git a/webapi/webapi.go b/webapi/webapi.go index e5e29ec..6c18ef7 100644 --- a/webapi/webapi.go +++ b/webapi/webapi.go @@ -67,6 +67,7 @@ type Server struct { cfg Config db *database.VspDatabase addrGen *addressGenerator + cache *cache signPrivKey ed25519.PrivateKey signPubKey ed25519.PublicKey } @@ -88,8 +89,8 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro } // Populate cached VSP stats before starting webserver. - initCache(base64.StdEncoding.EncodeToString(s.signPubKey)) - err = updateCache(ctx, vdb, dcrd, config.NetParams, wallets) + s.cache = newCache(base64.StdEncoding.EncodeToString(s.signPubKey)) + err = s.cache.update(ctx, vdb, dcrd, wallets) if err != nil { log.Errorf("Could not initialize VSP stats cache: %v", err) } @@ -174,7 +175,7 @@ func Start(ctx context.Context, requestShutdown func(), shutdownWg *sync.WaitGro shutdownWg.Done() return case <-time.After(refresh): - err := updateCache(ctx, vdb, dcrd, config.NetParams, wallets) + err := s.cache.update(ctx, vdb, dcrd, wallets) if err != nil { log.Errorf("Failed to update cached VSP stats: %v", err) } @@ -230,11 +231,11 @@ func (s *Server) router(cookieSecret []byte, dcrd rpc.DcrdConnect, wallets rpc.W api := router.Group("/api/v3") api.GET("/vspinfo", s.vspInfo) - api.POST("/setaltsignaddr", withDcrdClient(dcrd, s.cfg), s.broadcastTicket, s.vspAuth, s.setAltSignAddr) - api.POST("/feeaddress", withDcrdClient(dcrd, s.cfg), s.broadcastTicket, s.vspAuth, s.feeAddress) - api.POST("/ticketstatus", withDcrdClient(dcrd, s.cfg), s.vspAuth, s.ticketStatus) - api.POST("/payfee", withDcrdClient(dcrd, s.cfg), s.vspAuth, s.payFee) - api.POST("/setvotechoices", withDcrdClient(dcrd, s.cfg), withWalletClients(wallets, s.cfg), s.vspAuth, s.setVoteChoices) + api.POST("/setaltsignaddr", withDcrdClient(dcrd), s.broadcastTicket, s.vspAuth, s.setAltSignAddr) + api.POST("/feeaddress", withDcrdClient(dcrd), s.broadcastTicket, s.vspAuth, s.feeAddress) + api.POST("/ticketstatus", withDcrdClient(dcrd), s.vspAuth, s.ticketStatus) + api.POST("/payfee", withDcrdClient(dcrd), s.vspAuth, s.payFee) + api.POST("/setvotechoices", withDcrdClient(dcrd), withWalletClients(wallets), s.vspAuth, s.setVoteChoices) // Website routes. @@ -246,16 +247,16 @@ func (s *Server) router(cookieSecret []byte, dcrd rpc.DcrdConnect, wallets rpc.W login.POST("", s.adminLogin) admin := router.Group("/admin").Use( - withWalletClients(wallets, s.cfg), withSession(cookieStore), s.requireAdmin, + withWalletClients(wallets), withSession(cookieStore), s.requireAdmin, ) - admin.GET("", withDcrdClient(dcrd, s.cfg), s.adminPage) - admin.POST("/ticket", withDcrdClient(dcrd, s.cfg), s.ticketSearch) + admin.GET("", withDcrdClient(dcrd), s.adminPage) + admin.POST("/ticket", withDcrdClient(dcrd), s.ticketSearch) admin.GET("/backup", s.downloadDatabaseBackup) admin.POST("/logout", s.adminLogout) // Require Basic HTTP Auth on /admin/status endpoint. basic := router.Group("/admin").Use( - withDcrdClient(dcrd, s.cfg), withWalletClients(wallets, s.cfg), gin.BasicAuth(gin.Accounts{ + withDcrdClient(dcrd), withWalletClients(wallets), gin.BasicAuth(gin.Accounts{ "admin": s.cfg.AdminPass, }), )