Tolerate dcrwallet connection failures.
If at least 1 wallet connection succeeds, vspd should proceed to use the connected wallet(s). Only error out if all wallet connections fail.
This commit is contained in:
parent
c7835e8811
commit
a6d9b79619
@ -108,12 +108,16 @@ func (n *NotificationHandler) Notify(method string, params json.RawMessage) erro
|
||||
return nil
|
||||
}
|
||||
|
||||
walletClients, err := n.Wallets.Clients(n.Ctx, n.NetParams)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
// If this fails, there is nothing more we can do. Return.
|
||||
walletClients, failedConnections := n.Wallets.Clients(n.Ctx, n.NetParams)
|
||||
if len(walletClients) == 0 {
|
||||
// If no wallet clients, there is nothing more we can do. Return.
|
||||
log.Error("Could not connect to any wallets")
|
||||
return nil
|
||||
}
|
||||
if failedConnections > 0 {
|
||||
log.Errorf("Failed to connect to %d wallet(s), proceeding with only %d",
|
||||
failedConnections, len(walletClients))
|
||||
}
|
||||
|
||||
for _, ticket := range unconfirmedFees {
|
||||
feeTx, err := n.dcrdClient.GetRawTransaction(ticket.FeeTxHash)
|
||||
|
||||
6
main.go
6
main.go
@ -79,9 +79,9 @@ func run(ctx context.Context) error {
|
||||
wallets := rpc.SetupWallet(ctx, &shutdownWg, cfg.WalletUser, cfg.WalletPass,
|
||||
cfg.WalletHosts, cfg.walletCert)
|
||||
// Dial once just to validate config.
|
||||
_, err = wallets.Clients(ctx, cfg.netParams.Params)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
_, failedConnections := wallets.Clients(ctx, cfg.netParams.Params)
|
||||
if failedConnections > 0 {
|
||||
log.Errorf("Failed RPC connection on %d of %d voting wallets", failedConnections, len(cfg.WalletHosts))
|
||||
requestShutdown()
|
||||
shutdownWg.Wait()
|
||||
return err
|
||||
|
||||
@ -2,7 +2,6 @@ package rpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
|
||||
wallettypes "decred.org/dcrwallet/rpc/jsonrpc/types"
|
||||
@ -35,22 +34,26 @@ func SetupWallet(ctx context.Context, shutdownWg *sync.WaitGroup, user, pass str
|
||||
return walletConnect
|
||||
}
|
||||
|
||||
// Clients creates an array of new WalletRPC client instances. Returns an error
|
||||
// if dialing any wallet fails, or if any wallet is misconfigured.
|
||||
func (w *WalletConnect) Clients(ctx context.Context, netParams *chaincfg.Params) ([]*WalletRPC, error) {
|
||||
walletClients := make([]*WalletRPC, len(*w))
|
||||
// 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, int) {
|
||||
walletClients := make([]*WalletRPC, 0)
|
||||
failedConnections := 0
|
||||
|
||||
for i := 0; i < len(*w); i++ {
|
||||
for _, connect := range []connect(*w) {
|
||||
|
||||
c, newConnection, err := []connect(*w)[i]()
|
||||
c, newConnection, err := connect()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dcrwallet connection error: %v", err)
|
||||
log.Errorf("dcrwallet connection error: %v", err)
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
|
||||
// If this is a reused connection, we don't need to validate the
|
||||
// dcrwallet config again.
|
||||
if !newConnection {
|
||||
walletClients[i] = &WalletRPC{c, ctx}
|
||||
walletClients = append(walletClients, &WalletRPC{c, ctx})
|
||||
continue
|
||||
}
|
||||
|
||||
@ -58,59 +61,64 @@ func (w *WalletConnect) Clients(ctx context.Context, netParams *chaincfg.Params)
|
||||
var verMap map[string]dcrdtypes.VersionResult
|
||||
err = c.Call(ctx, "version", &verMap)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("version check on dcrwallet '%s' failed: %v",
|
||||
c.String(), err)
|
||||
log.Errorf("version check on dcrwallet '%s' failed: %v", c.String(), err)
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
walletVersion, exists := verMap["dcrwalletjsonrpcapi"]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("version response on dcrwallet '%s' missing 'dcrwalletjsonrpcapi'",
|
||||
log.Errorf("version response on dcrwallet '%s' missing 'dcrwalletjsonrpcapi'",
|
||||
c.String())
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
if walletVersion.VersionString != requiredWalletVersion {
|
||||
return nil, fmt.Errorf("dcrwallet '%s' has wrong RPC version: got %s, expected %s",
|
||||
log.Errorf("dcrwallet '%s' has wrong RPC version: got %s, expected %s",
|
||||
c.String(), walletVersion.VersionString, requiredWalletVersion)
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify dcrwallet is voting, unlocked, and is connected to dcrd (not SPV).
|
||||
// Verify dcrwallet is voting and unlocked.
|
||||
var walletInfo wallettypes.WalletInfoResult
|
||||
err = c.Call(ctx, "walletinfo", &walletInfo)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("walletinfo check on dcrwallet '%s' failed: %v",
|
||||
c.String(), err)
|
||||
log.Errorf("walletinfo check on dcrwallet '%s' failed: %v", c.String(), err)
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
|
||||
// TODO: The following 3 checks should probably just log a warning/error and
|
||||
// not return.
|
||||
// addtransaction and setvotechoice can still be used with a locked wallet.
|
||||
// importprivkey will fail if wallet is locked.
|
||||
|
||||
if !walletInfo.Voting {
|
||||
return nil, fmt.Errorf("wallet '%s' has voting disabled", c.String())
|
||||
// All wallet RPCs can still be used if voting is disabled, so just
|
||||
// log an error here. Don't count this as a failed connection.
|
||||
log.Errorf("wallet '%s' has voting disabled", c.String())
|
||||
}
|
||||
if !walletInfo.Unlocked {
|
||||
return nil, fmt.Errorf("wallet '%s' is not unlocked", c.String())
|
||||
}
|
||||
if !walletInfo.DaemonConnected {
|
||||
return nil, fmt.Errorf("wallet '%s' is not connected to dcrd", c.String())
|
||||
// If wallet is locked, ImportPrivKey cannot be used.
|
||||
log.Errorf("wallet '%s' is not unlocked", c.String())
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
|
||||
// Verify dcrwallet is on the correct network.
|
||||
var netID wire.CurrencyNet
|
||||
err = c.Call(ctx, "getcurrentnet", &netID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("getcurrentnet check on dcrwallet '%s' failed: %v",
|
||||
c.String(), err)
|
||||
log.Errorf("getcurrentnet check on dcrwallet '%s' failed: %v", c.String(), err)
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
if netID != netParams.Net {
|
||||
return nil, fmt.Errorf("dcrwallet '%s' running on %s, expected %s",
|
||||
c.String(), netID, netParams.Net)
|
||||
log.Errorf("dcrwallet '%s' running on %s, expected %s", c.String(), netID, netParams.Net)
|
||||
failedConnections++
|
||||
continue
|
||||
}
|
||||
|
||||
walletClients[i] = &WalletRPC{c, ctx}
|
||||
walletClients = append(walletClients, &WalletRPC{c, ctx})
|
||||
|
||||
}
|
||||
|
||||
return walletClients, nil
|
||||
return walletClients, failedConnections
|
||||
}
|
||||
|
||||
func (c *WalletRPC) AddTransaction(blockHash, txHex string) error {
|
||||
|
||||
@ -31,12 +31,16 @@ func withDcrdClient() gin.HandlerFunc {
|
||||
// context for downstream handlers to make use of.
|
||||
func withWalletClients() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
clients, err := wallets.Clients(c, cfg.NetParams)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
clients, failedConnections := wallets.Clients(c, cfg.NetParams)
|
||||
if len(clients) == 0 {
|
||||
log.Error("Could not connect to any wallets")
|
||||
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
||||
return
|
||||
}
|
||||
if failedConnections > 0 {
|
||||
log.Errorf("Failed to connect to %d wallet(s), proceeding with only %d",
|
||||
failedConnections, len(clients))
|
||||
}
|
||||
c.Set("WalletClients", clients)
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,9 +61,9 @@ func setVoteChoices(c *gin.Context) {
|
||||
// TODO: This will error if the wallet does not know about the ticket yet.
|
||||
// TODO: We shouldn't return on first error - we want to try all wallets.
|
||||
if err != nil {
|
||||
// If this fails, we still want to try the other wallets, so
|
||||
// don't return an error response, just log an error.
|
||||
log.Errorf("SetVoteChoice failed: %v", err)
|
||||
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user