Skip to content

Commit

Permalink
Fix the ticket buy configuration setup
Browse files Browse the repository at this point in the history
  • Loading branch information
dmigwi committed Jun 29, 2024
1 parent f3066cd commit 8ad416e
Show file tree
Hide file tree
Showing 6 changed files with 89 additions and 102 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ require (
github.com/decred/dcrdata/v8 v8.0.0-20240606003156-1f13820ad44a
github.com/decred/politeia v1.4.0
github.com/decred/slog v1.2.0
github.com/decred/vspd/client/v3 v3.0.0
github.com/decred/vspd/types/v2 v2.1.0
github.com/dgraph-io/badger v1.6.2
github.com/gen2brain/beeep v0.0.0-20220402123239-6a3042f4b71a
Expand Down Expand Up @@ -121,7 +122,6 @@ require (
github.com/decred/dcrtime v0.0.0-20191018193024-8d8b4ef0458e // indirect
github.com/decred/go-socks v1.1.0 // indirect
github.com/decred/vspd/client/v2 v2.1.0 // indirect
github.com/decred/vspd/client/v3 v3.0.0 // indirect
github.com/decred/vspd/types v1.1.0 // indirect
github.com/dgraph-io/ristretto v0.0.2 // indirect
github.com/dustin/go-humanize v1.0.1-0.20210705192016-249ff6c91207 // indirect
Expand Down
59 changes: 31 additions & 28 deletions libwallet/assets/dcr/ticket.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/crypto-power/cryptopower/libwallet/utils"
"github.com/decred/dcrd/chaincfg/chainhash"
"github.com/decred/dcrd/dcrutil/v4"
"github.com/decred/dcrd/wire"
vspd "github.com/decred/vspd/types/v2"
)

Expand Down Expand Up @@ -124,13 +123,15 @@ func (asset *Asset) PurchaseTickets(account, numTickets int32, vspHost, passphra
defer asset.LockWallet()

request := &w.PurchaseTicketsRequest{
Count: int(numTickets),
SourceAccount: uint32(account),
MinConf: asset.RequiredConfirmations(),
VSPFeePercent: vspClient.FeePercentage,
VSPFeePaymentProcess: func(ctx context.Context, ticket *w.VSPTicket, feeTx *wire.MsgTx) error {
return vspClient.Process(ctx, ticket, feeTx)
},
Count: int(numTickets),
SourceAccount: uint32(account),
MinConf: asset.RequiredConfirmations(),
VSPFeePercent: vspClient.FeePercentage,
VSPFeePaymentProcess: vspClient.Process,

// VotingAccount used to derive addresses for specifying voting rights.
// It is used when VotingAddress == nil, or Mixing == true
VotingAccount: uint32(account),
}

csppCfg := asset.readCSPPConfig()
Expand Down Expand Up @@ -240,49 +241,49 @@ func (asset *Asset) StartTicketBuyer(passphrase string) error {
return errors.New("Negative balance to maintain in ticket buyer config")
}

asset.cancelAutoTicketBuyerMu.Lock()
if asset.cancelAutoTicketBuyer != nil {
asset.cancelAutoTicketBuyerMu.Unlock()
if asset.IsAutoTicketsPurchaseActive() {
return errors.New("Ticket buyer already running")
}

// Validate the passphrase.
if len(passphrase) > 0 && asset.IsLocked() {
err := asset.UnlockWallet(passphrase)
if err != nil {
asset.cancelAutoTicketBuyerMu.Unlock()
return utils.TranslateError(err)
}
}

ctx, cancel := asset.ShutdownContextWithCancel()
asset.cancelAutoTicketBuyerMu.Lock()
asset.cancelAutoTicketBuyer = cancel
asset.cancelAutoTicketBuyerMu.Unlock()

// Check the VSP.
vspInfo, err := vspInfo(cfg.VspHost)
if err == nil {
cfg.VspClient, err = asset.VSPClient(cfg.PurchaseAccount, cfg.VspHost, vspInfo.PubKey)
}
if err != nil {
return fmt.Errorf("error setting up vsp client: %v", err)
}

cfg.VspClient, err = asset.VSPClient(cfg.PurchaseAccount, cfg.VspHost, vspInfo.PubKey)
if err != nil {
log.Errorf("[%d] VSP Client instance failed error: %v", asset.ID, err)
return errors.New("VSP Client failed to start due to incorrect configuration")
}

go func() {
log.Infof("[%d] Running ticket buyer", asset.ID)

err := asset.runTicketBuyer(ctx, passphrase, cfg)
if err != nil {
if err = asset.runTicketBuyer(ctx, passphrase, cfg); err != nil {
if ctx.Err() != nil {
log.Errorf("[%d] Ticket buyer instance canceled", asset.ID)
} else {
log.Errorf("[%d] Ticket buyer instance errored: %v", asset.ID, err)
}
}

asset.cancelAutoTicketBuyerMu.Lock()
asset.cancelAutoTicketBuyer = nil
asset.cancelAutoTicketBuyerMu.Unlock()
if err = asset.StopAutoTicketsPurchase(); err != nil {
log.Errorf("[%d] Stopping auto ticket purchase errored: %v", asset.ID, err)
}
}()

return nil
Expand Down Expand Up @@ -455,14 +456,16 @@ func (asset *Asset) buyTicket(ctx context.Context, passphrase string, sdiff dcru
// which can be used to link the tickets eventually purchased with the
// split outputs.
request := &w.PurchaseTicketsRequest{
Count: 1,
SourceAccount: uint32(cfg.PurchaseAccount),
Expiry: expiry,
MinConf: asset.RequiredConfirmations(),
VSPFeePercent: cfg.VspClient.FeePercentage,
VSPFeePaymentProcess: func(ctx context.Context, ticket *w.VSPTicket, feeTx *wire.MsgTx) error {
return cfg.VspClient.Process(ctx, ticket, feeTx)
},
Count: 1,
SourceAccount: uint32(cfg.PurchaseAccount),
Expiry: expiry,
MinConf: asset.RequiredConfirmations(),
VSPFeePercent: cfg.VspClient.FeePercentage,
VSPFeePaymentProcess: cfg.VspClient.Process,

// VotingAccount used to derive addresses for specifying voting rights.
// It is used when VotingAddress == nil, or Mixing == true
VotingAccount: uint32(cfg.PurchaseAccount),
}

csppCfg := asset.readCSPPConfig()
Expand Down
15 changes: 2 additions & 13 deletions libwallet/assets/dcr/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/decred/dcrd/dcrutil/v4"
vspd "github.com/decred/vspd/types/v2"
)

// Amount implements the Asset amount interface for the DCR asset
Expand Down Expand Up @@ -148,22 +149,10 @@ type VSPTicketInfo struct {
/** end politea proposal types */

/** begin vspd-related types */
type VspInfoResponse struct {
APIVersions []int64 `json:"apiversions"`
Timestamp int64 `json:"timestamp"`
PubKey []byte `json:"pubkey"`
FeePercentage float64 `json:"feepercentage"`
VspClosed bool `json:"vspclosed"`
Network string `json:"network"`
VspdVersion string `json:"vspdversion"`
Voting int64 `json:"voting"`
Voted int64 `json:"voted"`
Revoked int64 `json:"revoked"`
}

type VSP struct {
Host string
*VspInfoResponse
*vspd.VspInfoResponse
}

/** end vspd-related types */
Expand Down
80 changes: 32 additions & 48 deletions libwallet/assets/dcr/vsp.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ package dcr

import (
"context"
"crypto/ed25519"
"encoding/base64"
"encoding/json"
"fmt"
"net/http"
"strings"

"decred.org/dcrwallet/v4/errors"
"decred.org/dcrwallet/v4/vsp"
sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet"
"github.com/crypto-power/cryptopower/libwallet/utils"
vspdClient "github.com/decred/vspd/client/v3"
vspd "github.com/decred/vspd/types/v2"
)

const (
Expand All @@ -25,18 +24,28 @@ func (asset *Asset) VSPClient(account int32, host string, pubKey []byte) (*vsp.C
return nil, utils.ErrDCRNotInitialized
}

asset.vspClientsMu.Lock()
defer asset.vspClientsMu.Unlock()
client, ok := asset.vspClients[host]
if ok {
asset.vspMu.Lock()
defer asset.vspMu.Unlock()
if client, ok := asset.vspClients[host]; ok {
return client, nil
}

client, err := asset.createVspClient(account, host, pubKey)
if err != nil {
return nil, err
}

asset.vspClients[host] = client
return client, nil
}

func (asset *Asset) createVspClient(account int32, host string, pubKey []byte) (*vsp.Client, error) {
cfg := vsp.Config{
URL: host,
PubKey: string(pubKey),
PubKey: base64.StdEncoding.EncodeToString(pubKey),
Dialer: nil, // optional, but consider providing a value
Wallet: asset.Internal().DCR,
Params: asset.Internal().DCR.ChainParams(),
}

// When the account number provided is greater than -1, it means that the
Expand All @@ -49,21 +58,15 @@ func (asset *Asset) VSPClient(account int32, host string, pubKey []byte) (*vsp.C
}
}

client, err := vsp.New(cfg, log)
if err != nil {
return nil, err
}

asset.vspClients[host] = client
return client, nil
return vsp.New(cfg, log)
}

// KnownVSPs returns a list of known VSPs. This list may be updated by calling
// ReloadVSPList. This method is safe for concurrent access.
func (asset *Asset) KnownVSPs() []*VSP {
asset.vspMu.RLock()
defer asset.vspMu.RUnlock()
return asset.vsps // TODO: Return a copy.
return asset.vsps
}

// SaveVSP marks a VSP as known and will be susbequently included as part of
Expand Down Expand Up @@ -134,7 +137,7 @@ func (asset *Asset) ReloadVSPList(ctx context.Context) {
defer log.Debugf("Reloaded list of known VSPs")

vspDbData := asset.getVSPDBData()
vspList := make(map[string]*VspInfoResponse)
vspList := make(map[string]*vspd.VspInfoResponse)
for _, host := range vspDbData.SavedHosts {
vspInfo, err := vspInfo(host)
if err != nil {
Expand All @@ -152,16 +155,13 @@ func (asset *Asset) ReloadVSPList(ctx context.Context) {
if err != nil {
log.Debugf("get default vsp list error: %v", err)
}
for _, host := range otherVSPHosts {
for url, VSPInfo := range otherVSPHosts {
host := "https://" + url
if _, wasAdded := vspList[host]; wasAdded {
continue
}
vspInfo, err := vspInfo(host)
if err != nil {
log.Debugf("vsp info error for %s: %v\n", host, err) // debug only, user didn't request this VSP
} else {
vspList[host] = vspInfo
}

vspList[host] = VSPInfo
if ctx.Err() != nil {
return // context canceled, abort
}
Expand All @@ -175,7 +175,7 @@ func (asset *Asset) ReloadVSPList(ctx context.Context) {
asset.vspMu.Unlock()
}

func vspInfo(vspHost string) (*VspInfoResponse, error) {
func vspInfo(vspHost string) (*vspd.VspInfoResponse, error) {
req := &utils.ReqConfig{
Method: http.MethodGet,
HTTPURL: vspHost + "/api/v3/vspinfo",
Expand All @@ -188,27 +188,19 @@ func vspInfo(vspHost string) (*VspInfoResponse, error) {
return nil, err
}

vspInfoResponse := new(VspInfoResponse)
vspInfoResponse := new(vspd.VspInfoResponse)
if err := json.Unmarshal(respBytes, vspInfoResponse); err != nil {
return nil, err
}

// Validate server response.
sigStr := resp.Header.Get("VSP-Server-Signature")
sig, err := base64.StdEncoding.DecodeString(sigStr)
if err != nil {
return nil, fmt.Errorf("error validating VSP signature: %v", err)
}
if !ed25519.Verify(vspInfoResponse.PubKey, respBytes, sig) {
return nil, errors.New("bad signature from VSP")
}

return vspInfoResponse, nil
err = vspdClient.ValidateServerSignature(resp, respBytes, vspInfoResponse.PubKey)
return vspInfoResponse, err
}

// defaultVSPs returns a list of known VSPs.
func defaultVSPs(network string) ([]string, error) {
var vspInfoResponse map[string]*VspInfoResponse
func defaultVSPs(network string) (map[string]*vspd.VspInfoResponse, error) {

Check failure on line 202 in libwallet/assets/dcr/vsp.go

View workflow job for this annotation

GitHub Actions / Build

unused-parameter: parameter 'network' seems to be unused, consider removing or renaming it as _ (revive)

Check warning on line 202 in libwallet/assets/dcr/vsp.go

View workflow job for this annotation

GitHub Actions / Build

unused-parameter: parameter 'network' seems to be unused, consider removing or renaming it as _ (revive)
var vspInfoResponse map[string]*vspd.VspInfoResponse
req := &utils.ReqConfig{
Method: http.MethodGet,
HTTPURL: defaultVSPsURL,
Expand All @@ -218,14 +210,6 @@ func defaultVSPs(network string) ([]string, error) {
return nil, err
}

// The above API does not return the pubKeys for the
// VSPs. Only return the host since we'll still need
// to make another API call to get the VSP pubKeys.
vsps := make([]string, 0)
for url, vspInfo := range vspInfoResponse {
if strings.Contains(network, vspInfo.Network) {
vsps = append(vsps, "https://"+url)
}
}
return vsps, nil
// The above API does not return the pubKeys for the VSPs.
return vspInfoResponse, nil
}
8 changes: 4 additions & 4 deletions libwallet/assets/dcr/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ type Asset struct {

TxAuthoredInfo *TxAuthor

vspClientsMu sync.Mutex
vspClients map[string]*vsp.Client
vspMu sync.RWMutex
vsps []*VSP
// VSP data
vspClients map[string]*vsp.Client
vspMu sync.RWMutex
vsps []*VSP

notificationListenersMu sync.RWMutex
syncData *SyncData
Expand Down
Loading

0 comments on commit 8ad416e

Please sign in to comment.