multi wallet support (#32)
* multiwallet * print host Co-authored-by: Jamie Holdstock <jholdstock@decred.org>
This commit is contained in:
parent
6a100811f4
commit
225dcaf29e
@ -14,7 +14,7 @@ import (
|
|||||||
type NotificationHandler struct {
|
type NotificationHandler struct {
|
||||||
Ctx context.Context
|
Ctx context.Context
|
||||||
Db *database.VspDatabase
|
Db *database.VspDatabase
|
||||||
WalletConnect rpc.Connect
|
WalletConnect []rpc.Connect
|
||||||
NetParams *chaincfg.Params
|
NetParams *chaincfg.Params
|
||||||
closed chan struct{}
|
closed chan struct{}
|
||||||
dcrdClient *rpc.DcrdRPC
|
dcrdClient *rpc.DcrdRPC
|
||||||
@ -108,18 +108,21 @@ func (n *NotificationHandler) Notify(method string, params json.RawMessage) erro
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var walletClient *rpc.WalletRPC
|
walletClients := make([]*rpc.WalletRPC, len(n.WalletConnect))
|
||||||
walletConn, err := n.WalletConnect()
|
for i := 0; i < len(n.WalletConnect); i++ {
|
||||||
if err != nil {
|
walletConn, err := n.WalletConnect[i]()
|
||||||
log.Errorf("dcrwallet connection error: %v", err)
|
if err != nil {
|
||||||
// If this fails, there is nothing more we can do. Return.
|
// TODO: what host?
|
||||||
return nil
|
log.Errorf("dcrwallet connection error: %v", err)
|
||||||
}
|
// If this fails, there is nothing more we can do. Return.
|
||||||
walletClient, err = rpc.WalletClient(n.Ctx, walletConn, n.NetParams)
|
return nil
|
||||||
if err != nil {
|
}
|
||||||
log.Errorf("dcrwallet client error: %v", err)
|
walletClients[i], err = rpc.WalletClient(n.Ctx, walletConn, n.NetParams)
|
||||||
// If this fails, there is nothing more we can do. Return.
|
if err != nil {
|
||||||
return nil
|
log.Errorf("dcrwallet '%s' client error: %v", walletConn.String(), err)
|
||||||
|
// If this fails, there is nothing more we can do. Return.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, ticket := range unconfirmedFees {
|
for _, ticket := range unconfirmedFees {
|
||||||
@ -147,26 +150,32 @@ func (n *NotificationHandler) Notify(method string, params json.RawMessage) erro
|
|||||||
log.Errorf("GetRawTransaction error: %v", err)
|
log.Errorf("GetRawTransaction error: %v", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = walletClient.AddTransaction(rawTicket.BlockHash, rawTicket.Hex)
|
for _, walletClient := range walletClients {
|
||||||
if err != nil {
|
err = walletClient.AddTransaction(rawTicket.BlockHash, rawTicket.Hex)
|
||||||
log.Errorf("AddTransaction error: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
err = walletClient.ImportPrivKey(ticket.VotingWIF)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("ImportPrivKey error: %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update vote choices on voting wallets.
|
|
||||||
for agenda, choice := range ticket.VoteChoices {
|
|
||||||
err = walletClient.SetVoteChoice(agenda, choice, ticket.Hash)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("SetVoteChoice error: %v", err)
|
log.Errorf("AddTransaction error on dcrwallet '%s': %v",
|
||||||
|
walletClient.String(), err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
err = walletClient.ImportPrivKey(ticket.VotingWIF)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("ImportPrivKey error on dcrwallet '%s': %v",
|
||||||
|
walletClient.String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update vote choices on voting wallets.
|
||||||
|
for agenda, choice := range ticket.VoteChoices {
|
||||||
|
err = walletClient.SetVoteChoice(agenda, choice, ticket.Hash)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("SetVoteChoice error on dcrwallet '%s': %v",
|
||||||
|
walletClient.String(), err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.Debugf("Ticket added to voting wallet '%s': ticketHash=%s",
|
||||||
|
walletClient.String(), ticket.Hash)
|
||||||
}
|
}
|
||||||
log.Debugf("Ticket added to voting wallet: ticketHash=%s", ticket.Hash)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
16
config.go
16
config.go
@ -45,7 +45,7 @@ type config struct {
|
|||||||
DcrdUser string `long:"dcrduser" ini-name:"dcrduser" description:"Username for dcrd RPC connections."`
|
DcrdUser string `long:"dcrduser" ini-name:"dcrduser" description:"Username for dcrd RPC connections."`
|
||||||
DcrdPass string `long:"dcrdpass" ini-name:"dcrdpass" description:"Password for dcrd RPC connections."`
|
DcrdPass string `long:"dcrdpass" ini-name:"dcrdpass" description:"Password for dcrd RPC connections."`
|
||||||
DcrdCert string `long:"dcrdcert" ini-name:"dcrdcert" description:"The dcrd RPC certificate file."`
|
DcrdCert string `long:"dcrdcert" ini-name:"dcrdcert" description:"The dcrd RPC certificate file."`
|
||||||
WalletHost string `long:"wallethost" ini-name:"wallethost" description:"The ip:port to establish a JSON-RPC connection with voting dcrwallet."`
|
WalletHosts []string `long:"wallethost" ini-name:"wallethost" description:"Add an ip:port to establish a JSON-RPC connection with voting dcrwallet."`
|
||||||
WalletUser string `long:"walletuser" ini-name:"walletuser" description:"Username for dcrwallet RPC connections."`
|
WalletUser string `long:"walletuser" ini-name:"walletuser" description:"Username for dcrwallet RPC connections."`
|
||||||
WalletPass string `long:"walletpass" ini-name:"walletpass" description:"Password for dcrwallet RPC connections."`
|
WalletPass string `long:"walletpass" ini-name:"walletpass" description:"Password for dcrwallet RPC connections."`
|
||||||
WalletCert string `long:"walletcert" ini-name:"walletcert" description:"The dcrwallet RPC certificate file."`
|
WalletCert string `long:"walletcert" ini-name:"walletcert" description:"The dcrwallet RPC certificate file."`
|
||||||
@ -155,7 +155,7 @@ func loadConfig() (*config, error) {
|
|||||||
HomeDir: defaultHomeDir,
|
HomeDir: defaultHomeDir,
|
||||||
ConfigFile: defaultConfigFile,
|
ConfigFile: defaultConfigFile,
|
||||||
DcrdHost: defaultDcrdHost,
|
DcrdHost: defaultDcrdHost,
|
||||||
WalletHost: defaultWalletHost,
|
WalletHosts: []string{defaultWalletHost},
|
||||||
WebServerDebug: defaultWebServerDebug,
|
WebServerDebug: defaultWebServerDebug,
|
||||||
BackupInterval: defaultBackupInterval,
|
BackupInterval: defaultBackupInterval,
|
||||||
VspClosed: defaultVspClosed,
|
VspClosed: defaultVspClosed,
|
||||||
@ -239,11 +239,13 @@ func loadConfig() (*config, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Set the active network.
|
// Set the active network.
|
||||||
|
minRequired := 1
|
||||||
switch cfg.Network {
|
switch cfg.Network {
|
||||||
case "testnet":
|
case "testnet":
|
||||||
cfg.netParams = &testNet3Params
|
cfg.netParams = &testNet3Params
|
||||||
case "mainnet":
|
case "mainnet":
|
||||||
cfg.netParams = &mainNetParams
|
cfg.netParams = &mainNetParams
|
||||||
|
minRequired = 3
|
||||||
case "simnet":
|
case "simnet":
|
||||||
cfg.netParams = &simNetParams
|
cfg.netParams = &simNetParams
|
||||||
}
|
}
|
||||||
@ -302,9 +304,17 @@ func loadConfig() (*config, error) {
|
|||||||
return nil, fmt.Errorf("failed to read dcrwallet cert file: %v", err)
|
return nil, fmt.Errorf("failed to read dcrwallet cert file: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Verify minimum number of voting wallets are configured.
|
||||||
|
if minRequired < len(cfg.WalletHosts) {
|
||||||
|
return nil, fmt.Errorf("minimum required voting wallets has not been met: %d < %d",
|
||||||
|
len(cfg.WalletHosts), minRequired)
|
||||||
|
}
|
||||||
|
|
||||||
// Add default port for the active network if there is no port specified.
|
// Add default port for the active network if there is no port specified.
|
||||||
|
for i := 0; i < len(cfg.WalletHosts); i++ {
|
||||||
|
cfg.WalletHosts[i] = normalizeAddress(cfg.WalletHosts[i], cfg.netParams.WalletRPCServerPort)
|
||||||
|
}
|
||||||
cfg.DcrdHost = normalizeAddress(cfg.DcrdHost, cfg.netParams.DcrdRPCServerPort)
|
cfg.DcrdHost = normalizeAddress(cfg.DcrdHost, cfg.netParams.DcrdRPCServerPort)
|
||||||
cfg.WalletHost = normalizeAddress(cfg.WalletHost, cfg.netParams.WalletRPCServerPort)
|
|
||||||
|
|
||||||
// Create the data directory.
|
// Create the data directory.
|
||||||
dataDir := filepath.Join(cfg.HomeDir, "data", cfg.netParams.Name)
|
dataDir := filepath.Join(cfg.HomeDir, "data", cfg.netParams.Name)
|
||||||
|
|||||||
34
main.go
34
main.go
@ -84,21 +84,25 @@ func run(ctx context.Context) error {
|
|||||||
|
|
||||||
// Create RPC client for remote dcrwallet instance (used for voting).
|
// Create RPC client for remote dcrwallet instance (used for voting).
|
||||||
// Dial once just to validate config.
|
// Dial once just to validate config.
|
||||||
walletConnect := rpc.Setup(ctx, &shutdownWg, cfg.WalletUser, cfg.WalletPass,
|
walletConnect := make([]rpc.Connect, len(cfg.WalletHosts))
|
||||||
cfg.WalletHost, cfg.walletCert, nil)
|
walletConn := make([]rpc.Caller, len(cfg.WalletHosts))
|
||||||
walletConn, err := walletConnect()
|
for i := 0; i < len(cfg.WalletHosts); i++ {
|
||||||
if err != nil {
|
walletConnect[i] = rpc.Setup(ctx, &shutdownWg, cfg.WalletUser, cfg.WalletPass,
|
||||||
log.Errorf("dcrwallet connection error: %v", err)
|
cfg.WalletHosts[i], cfg.walletCert, nil)
|
||||||
requestShutdown()
|
walletConn[i], err = walletConnect[i]()
|
||||||
shutdownWg.Wait()
|
if err != nil {
|
||||||
return err
|
log.Errorf("dcrwallet '%s' connection error: %v", cfg.WalletHosts[i], err)
|
||||||
}
|
requestShutdown()
|
||||||
_, err = rpc.WalletClient(ctx, walletConn, cfg.netParams.Params)
|
shutdownWg.Wait()
|
||||||
if err != nil {
|
return err
|
||||||
log.Errorf("dcrwallet client error: %v", err)
|
}
|
||||||
requestShutdown()
|
_, err = rpc.WalletClient(ctx, walletConn[i], cfg.netParams.Params)
|
||||||
shutdownWg.Wait()
|
if err != nil {
|
||||||
return err
|
log.Errorf("dcrwallet '%s' client error: %v", cfg.WalletHosts[i], err)
|
||||||
|
requestShutdown()
|
||||||
|
shutdownWg.Wait()
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a dcrd client with an attached notification handler which will run
|
// Create a dcrd client with an attached notification handler which will run
|
||||||
|
|||||||
@ -11,6 +11,9 @@ import (
|
|||||||
|
|
||||||
// Caller provides a client interface to perform JSON-RPC remote procedure calls.
|
// Caller provides a client interface to perform JSON-RPC remote procedure calls.
|
||||||
type Caller interface {
|
type Caller interface {
|
||||||
|
// String returns the dialed URL.
|
||||||
|
String() string
|
||||||
|
|
||||||
// Call performs the remote procedure call defined by method and
|
// Call performs the remote procedure call defined by method and
|
||||||
// waits for a response or a broken client connection.
|
// waits for a response or a broken client connection.
|
||||||
// Args provides positional parameters for the call.
|
// Args provides positional parameters for the call.
|
||||||
|
|||||||
@ -28,41 +28,46 @@ func WalletClient(ctx context.Context, c Caller, netParams *chaincfg.Params) (*W
|
|||||||
var verMap map[string]dcrdtypes.VersionResult
|
var verMap map[string]dcrdtypes.VersionResult
|
||||||
err := c.Call(ctx, "version", &verMap)
|
err := c.Call(ctx, "version", &verMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("version check failed: %v", err)
|
return nil, fmt.Errorf("version check on dcrwallet '%s' failed: %v",
|
||||||
|
c.String(), err)
|
||||||
}
|
}
|
||||||
walletVersion, exists := verMap["dcrwalletjsonrpcapi"]
|
walletVersion, exists := verMap["dcrwalletjsonrpcapi"]
|
||||||
if !exists {
|
if !exists {
|
||||||
return nil, fmt.Errorf("version response missing 'dcrwalletjsonrpcapi'")
|
return nil, fmt.Errorf("version response on dcrwallet '%s' missing 'dcrwalletjsonrpcapi'",
|
||||||
|
c.String())
|
||||||
}
|
}
|
||||||
if walletVersion.VersionString != requiredWalletVersion {
|
if walletVersion.VersionString != requiredWalletVersion {
|
||||||
return nil, fmt.Errorf("wrong dcrwallet RPC version: got %s, expected %s",
|
return nil, fmt.Errorf("dcrwallet '%s' has wrong RPC version: got %s, expected %s",
|
||||||
walletVersion.VersionString, requiredWalletVersion)
|
c.String(), walletVersion.VersionString, requiredWalletVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify dcrwallet is voting, unlocked, and is connected to dcrd (not SPV).
|
// Verify dcrwallet is voting, unlocked, and is connected to dcrd (not SPV).
|
||||||
var walletInfo wallettypes.WalletInfoResult
|
var walletInfo wallettypes.WalletInfoResult
|
||||||
err = c.Call(ctx, "walletinfo", &walletInfo)
|
err = c.Call(ctx, "walletinfo", &walletInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("walletinfo check failed: %v", err)
|
return nil, fmt.Errorf("walletinfo check on dcrwallet '%s' failed: %v",
|
||||||
|
c.String(), err)
|
||||||
}
|
}
|
||||||
if !walletInfo.Voting {
|
if !walletInfo.Voting {
|
||||||
return nil, fmt.Errorf("wallet has voting disabled")
|
return nil, fmt.Errorf("wallet '%s' has voting disabled", c.String())
|
||||||
}
|
}
|
||||||
if !walletInfo.Unlocked {
|
if !walletInfo.Unlocked {
|
||||||
return nil, fmt.Errorf("wallet is not unlocked")
|
return nil, fmt.Errorf("wallet '%s' is not unlocked", c.String())
|
||||||
}
|
}
|
||||||
if !walletInfo.DaemonConnected {
|
if !walletInfo.DaemonConnected {
|
||||||
return nil, fmt.Errorf("wallet is not connected to dcrd")
|
return nil, fmt.Errorf("wallet '%s' is not connected to dcrd", c.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify dcrwallet is on the correct network.
|
// Verify dcrwallet is on the correct network.
|
||||||
var netID wire.CurrencyNet
|
var netID wire.CurrencyNet
|
||||||
err = c.Call(ctx, "getcurrentnet", &netID)
|
err = c.Call(ctx, "getcurrentnet", &netID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("getcurrentnet check failed: %v", err)
|
return nil, fmt.Errorf("getcurrentnet check on dcrwallet '%s' failed: %v",
|
||||||
|
c.String(), err)
|
||||||
}
|
}
|
||||||
if netID != netParams.Net {
|
if netID != netParams.Net {
|
||||||
return nil, fmt.Errorf("dcrwallet running on %s, expected %s", netID, netParams.Net)
|
return nil, fmt.Errorf("dcrwallet '%s' running on %s, expected %s",
|
||||||
|
c.String(), netID, netParams.Net)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &WalletRPC{c, ctx}, nil
|
return &WalletRPC{c, ctx}, nil
|
||||||
|
|||||||
@ -37,19 +37,21 @@ func withDcrdClient() gin.HandlerFunc {
|
|||||||
// context for downstream handlers to make use of.
|
// context for downstream handlers to make use of.
|
||||||
func withWalletClient() gin.HandlerFunc {
|
func withWalletClient() gin.HandlerFunc {
|
||||||
return func(c *gin.Context) {
|
return func(c *gin.Context) {
|
||||||
walletConn, err := walletConnect()
|
walletClient := make([]*rpc.WalletRPC, len(walletConnect))
|
||||||
if err != nil {
|
for i := 0; i < len(walletConnect); i++ {
|
||||||
log.Errorf("dcrwallet connection error: %v", err)
|
walletConn, err := walletConnect[i]()
|
||||||
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
if err != nil {
|
||||||
return
|
log.Errorf("dcrwallet '%s' connection error: %v", err)
|
||||||
|
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
walletClient[i], err = rpc.WalletClient(c, walletConn, cfg.NetParams)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("dcrwallet '%s' client error: %v", walletClient[i].String(), err)
|
||||||
|
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
walletClient, err := rpc.WalletClient(c, walletConn, cfg.NetParams)
|
|
||||||
if err != nil {
|
|
||||||
log.Errorf("dcrwallet client error: %v", err)
|
|
||||||
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
c.Set("WalletClient", walletClient)
|
c.Set("WalletClient", walletClient)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,7 +17,7 @@ func setVoteChoices(c *gin.Context) {
|
|||||||
rawRequest := c.MustGet("RawRequest").([]byte)
|
rawRequest := c.MustGet("RawRequest").([]byte)
|
||||||
ticket := c.MustGet("Ticket").(database.Ticket)
|
ticket := c.MustGet("Ticket").(database.Ticket)
|
||||||
knownTicket := c.MustGet("KnownTicket").(bool)
|
knownTicket := c.MustGet("KnownTicket").(bool)
|
||||||
walletClient := c.MustGet("WalletClient").(*rpc.WalletRPC)
|
walletClients := c.MustGet("WalletClient").([]*rpc.WalletRPC)
|
||||||
|
|
||||||
if !knownTicket {
|
if !knownTicket {
|
||||||
log.Warnf("Invalid ticket from %s", c.ClientIP())
|
log.Warnf("Invalid ticket from %s", c.ClientIP())
|
||||||
@ -56,11 +56,13 @@ func setVoteChoices(c *gin.Context) {
|
|||||||
// wallets if their fee is confirmed.
|
// wallets if their fee is confirmed.
|
||||||
if ticket.FeeConfirmed {
|
if ticket.FeeConfirmed {
|
||||||
for agenda, choice := range voteChoices {
|
for agenda, choice := range voteChoices {
|
||||||
err = walletClient.SetVoteChoice(agenda, choice, ticket.Hash)
|
for _, walletClient := range walletClients {
|
||||||
if err != nil {
|
err = walletClient.SetVoteChoice(agenda, choice, ticket.Hash)
|
||||||
log.Errorf("SetVoteChoice failed: %v", err)
|
if err != nil {
|
||||||
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
log.Errorf("SetVoteChoice failed: %v", err)
|
||||||
return
|
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -38,13 +38,13 @@ const (
|
|||||||
var cfg Config
|
var cfg Config
|
||||||
var db *database.VspDatabase
|
var db *database.VspDatabase
|
||||||
var dcrdConnect rpc.Connect
|
var dcrdConnect rpc.Connect
|
||||||
var walletConnect rpc.Connect
|
var walletConnect []rpc.Connect
|
||||||
var addrGen *addressGenerator
|
var addrGen *addressGenerator
|
||||||
var signPrivKey ed25519.PrivateKey
|
var signPrivKey ed25519.PrivateKey
|
||||||
var signPubKey ed25519.PublicKey
|
var signPubKey ed25519.PublicKey
|
||||||
|
|
||||||
func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *sync.WaitGroup,
|
func Start(ctx context.Context, requestShutdownChan chan struct{}, shutdownWg *sync.WaitGroup,
|
||||||
listen string, vdb *database.VspDatabase, dConnect rpc.Connect, wConnect rpc.Connect, debugMode bool, feeXPub string, config Config) error {
|
listen string, vdb *database.VspDatabase, dConnect rpc.Connect, wConnect []rpc.Connect, debugMode bool, feeXPub string, config Config) error {
|
||||||
|
|
||||||
cfg = config
|
cfg = config
|
||||||
db = vdb
|
db = vdb
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user