diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 7eb1e6a48..a515ea0f6 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -6,12 +6,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out code into the Go module directory - uses: actions/checkout@v1 + uses: actions/checkout@v3 - - name: Set up Go 1.19 - uses: actions/setup-go@v1 + - name: Set up Go 1.20 + uses: actions/setup-go@v3 with: - go-version: 1.19 + go-version: "1.20" id: go - name: Libraries for gio @@ -20,13 +20,13 @@ jobs: sudo apt install libwayland-dev libx11-dev libx11-xcb-dev libxkbcommon-x11-dev libgles2-mesa-dev libegl1-mesa-dev libffi-dev libxcursor-dev libvulkan-dev - name: Cache (dependencies) - uses: actions/cache@v1 + uses: actions/cache@v3 id: cache with: path: ~/go/pkg/mod - key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}-v2 + key: ${{ runner.os }}-go-v2-${{ hashFiles('**/go.sum') }} restore-keys: | - ${{ runner.os }}-go- + ${{ runner.os }}-go-v2 - name: Install linter run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s v1.53.3 diff --git a/go.mod b/go.mod index 524f6b5a6..8dc37b685 100644 --- a/go.mod +++ b/go.mod @@ -1,9 +1,9 @@ module github.com/crypto-power/cryptopower -go 1.19 +go 1.20 require ( - decred.org/dcrwallet/v3 v3.0.1 + decred.org/dcrwallet/v4 v4.0.0-20230809150859-a87fa843495e gioui.org v0.1.0 github.com/JohannesKaufmann/html-to-markdown v1.2.1 github.com/PuerkitoBio/goquery v1.6.1 @@ -37,6 +37,8 @@ require ( github.com/decred/dcrdata/v8 v8.0.0-20230617164141-fa4d8e1b4e8e github.com/decred/politeia v1.4.0 github.com/decred/slog v1.2.0 + github.com/decred/vspd/client/v2 v2.0.0 + github.com/decred/vspd/types/v2 v2.0.0 github.com/dgraph-io/badger v1.6.2 github.com/gen2brain/beeep v0.0.0-20220402123239-6a3042f4b71a github.com/gomarkdown/markdown v0.0.0-20220817224203-2206187d3406 @@ -100,8 +102,6 @@ require ( github.com/decred/dcrd/txscript/v3 v3.0.0 // indirect 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.0.0 // indirect - github.com/decred/vspd/types/v2 v2.0.0 // indirect github.com/dgraph-io/ristretto v0.0.2 // indirect github.com/dustin/go-humanize v1.0.1-0.20210705192016-249ff6c91207 // indirect github.com/fogleman/gg v1.3.0 // indirect diff --git a/go.sum b/go.sum index 81193a8e3..af5092e88 100644 --- a/go.sum +++ b/go.sum @@ -79,8 +79,8 @@ decred.org/cspp/v2 v2.1.0 h1:HeHb9+BFqrBaAPc6CsPiUpPFmC1uyBM2mJZUAbUXkRw= decred.org/cspp/v2 v2.1.0/go.mod h1:9nO3bfvCheOPIFZw5f6sRQ42CjBFB5RKSaJ9Iq6G4MA= decred.org/dcrwallet v1.7.0 h1:U/ew00YBdUlx3rJAynt2OdKDgGzBKK4O89FijBq8iVg= decred.org/dcrwallet v1.7.0/go.mod h1:hNOGyvH53gWdgFB601/ubGRzCPfPtWnEVAi9Grs90y4= -decred.org/dcrwallet/v3 v3.0.1 h1:+OLi+u/MvKc3Ubcnf19oyG/a5hJ/qp4OtezdiQZnLIs= -decred.org/dcrwallet/v3 v3.0.1/go.mod h1:a+R8BZIOKVpWVPat5VZoBWNh/cnIciwcRkPtrzfS/tw= +decred.org/dcrwallet/v4 v4.0.0-20230809150859-a87fa843495e h1:Y1f5xYRQi7qMzLYifvAOplMhgHiKxtWWzd1psXqsS6A= +decred.org/dcrwallet/v4 v4.0.0-20230809150859-a87fa843495e/go.mod h1:0+CchVf/baDYJ0tlDjtEzAFsvYcwWRm0eo1+Lf7Z1as= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= eliasnaur.com/font v0.0.0-20230308162249-dd43949cb42d h1:ARo7NCVvN2NdhLlJE9xAbKweuI9L6UgfTbYb0YwPacY= gioui.org v0.1.0 h1:fEDY5A4+epOdzjCBYSUC4BzvjWqsjfqf5D6mskbthOs= diff --git a/libwallet/assets/btc/accounts.go b/libwallet/assets/btc/accounts.go index 6e653e1eb..842e156e9 100644 --- a/libwallet/assets/btc/accounts.go +++ b/libwallet/assets/btc/accounts.go @@ -7,7 +7,7 @@ import ( "strconv" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcwallet/waddrmgr" diff --git a/libwallet/assets/btc/address.go b/libwallet/assets/btc/address.go index 9edf2c127..28b5e6372 100644 --- a/libwallet/assets/btc/address.go +++ b/libwallet/assets/btc/address.go @@ -3,7 +3,7 @@ package btc import ( "fmt" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/btcutil" "github.com/crypto-power/cryptopower/libwallet/utils" ) diff --git a/libwallet/assets/btc/feerate.go b/libwallet/assets/btc/feerate.go index bd41d428a..609a26f3d 100644 --- a/libwallet/assets/btc/feerate.go +++ b/libwallet/assets/btc/feerate.go @@ -7,7 +7,7 @@ import ( "strconv" "sync" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/btcutil" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/btc/rescan.go b/libwallet/assets/btc/rescan.go index 9843805fa..4d3608d02 100644 --- a/libwallet/assets/btc/rescan.go +++ b/libwallet/assets/btc/rescan.go @@ -6,7 +6,7 @@ import ( "sync/atomic" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcwallet/chain" "github.com/btcsuite/btcwallet/waddrmgr" diff --git a/libwallet/assets/btc/sync.go b/libwallet/assets/btc/sync.go index f1be049e3..23a5f5e3d 100644 --- a/libwallet/assets/btc/sync.go +++ b/libwallet/assets/btc/sync.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcwallet/chain" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/btc/txandblocknotifications.go b/libwallet/assets/btc/txandblocknotifications.go index 8974090ca..de0ad3826 100644 --- a/libwallet/assets/btc/txandblocknotifications.go +++ b/libwallet/assets/btc/txandblocknotifications.go @@ -4,7 +4,7 @@ import ( "encoding/json" "sync/atomic" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" ) diff --git a/libwallet/assets/btc/txauthor.go b/libwallet/assets/btc/txauthor.go index 0d3941ca6..62b021755 100644 --- a/libwallet/assets/btc/txauthor.go +++ b/libwallet/assets/btc/txauthor.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" diff --git a/libwallet/assets/btc/wallet.go b/libwallet/assets/btc/wallet.go index 90ecee53c..affa0430b 100644 --- a/libwallet/assets/btc/wallet.go +++ b/libwallet/assets/btc/wallet.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/btcutil/gcs" diff --git a/libwallet/assets/dcr/account_mixer.go b/libwallet/assets/dcr/account_mixer.go index c2d9c561a..6024ff268 100644 --- a/libwallet/assets/dcr/account_mixer.go +++ b/libwallet/assets/dcr/account_mixer.go @@ -7,9 +7,9 @@ import ( "errors" "net" - "decred.org/dcrwallet/v3/ticketbuyer" - w "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/udb" + "decred.org/dcrwallet/v4/ticketbuyer" + w "decred.org/dcrwallet/v4/wallet" + "decred.org/dcrwallet/v4/wallet/udb" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/internal/certs" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/dcr/accounts.go b/libwallet/assets/dcr/accounts.go index 99d9b2c83..d631f45fc 100644 --- a/libwallet/assets/dcr/accounts.go +++ b/libwallet/assets/dcr/accounts.go @@ -6,8 +6,8 @@ import ( "fmt" "strconv" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" "github.com/crypto-power/cryptopower/libwallet/addresshelper" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/dcr/address.go b/libwallet/assets/dcr/address.go index d041cdeaf..b6f72da27 100644 --- a/libwallet/assets/dcr/address.go +++ b/libwallet/assets/dcr/address.go @@ -3,8 +3,8 @@ package dcr import ( "fmt" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/txscript/v4/stdaddr" ) diff --git a/libwallet/assets/dcr/consensus.go b/libwallet/assets/dcr/consensus.go index 04ed1e486..e5de08846 100644 --- a/libwallet/assets/dcr/consensus.go +++ b/libwallet/assets/dcr/consensus.go @@ -6,8 +6,7 @@ import ( "sort" "strings" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/chaincfg/chainhash" @@ -111,24 +110,21 @@ func (asset *Asset) SetVoteChoice(agendaID, choiceID, hash, passphrase string) e return err } - currentChoice := w.AgendaChoice{ - AgendaID: agendaID, - ChoiceID: "abstain", // default to abstain as current choice if not found in wallet + currentChoice, ok := choices[agendaID] + if ok && currentChoice == strings.ToLower(choiceID) { + // Do not set the same choice again + return nil } - for i := range choices { - if choices[i].AgendaID == agendaID { - currentChoice.ChoiceID = choices[i].ChoiceID - break - } + if !ok { + // Default to abstain if no previous choice existed + currentChoice = "abstain" } - newChoice := w.AgendaChoice{ - AgendaID: agendaID, - ChoiceID: strings.ToLower(choiceID), - } + currentChoiceMap := map[string]string{agendaID: currentChoice} + newChoiceMap := map[string]string{agendaID: strings.ToLower(choiceID)} - _, err = asset.Internal().DCR.SetAgendaChoices(ctx, ticketHash, newChoice) + _, err = asset.Internal().DCR.SetAgendaChoices(ctx, ticketHash, newChoiceMap) if err != nil { return err } @@ -138,7 +134,7 @@ func (asset *Asset) SetVoteChoice(agendaID, choiceID, hash, passphrase string) e if !vspPreferenceUpdateSuccess { // Updating the agenda voting preference with the vsp failed, // revert the locally saved voting preference for the agenda. - _, revertError := asset.Internal().DCR.SetAgendaChoices(ctx, ticketHash, currentChoice) + _, revertError := asset.Internal().DCR.SetAgendaChoices(ctx, ticketHash, currentChoiceMap) if revertError != nil { log.Errorf("unable to revert locally saved voting preference: %v", revertError) } @@ -181,7 +177,7 @@ func (asset *Asset) SetVoteChoice(agendaID, choiceID, hash, passphrase string) e firstErr = err continue // try next tHash } - err = vspClient.SetVoteChoice(ctx, tHash, []w.AgendaChoice{newChoice}, nil, nil) + err = vspClient.SetVoteChoice(ctx, tHash, newChoiceMap, nil, nil) if err != nil && firstErr == nil { firstErr = err continue // try next tHash @@ -245,9 +241,9 @@ func (asset *Asset) AllVoteAgendas(hash string, newestFirst bool) ([]*Agenda, er d := &deployments[i] votingPreference := "abstain" // assume abstain, if we have the saved pref, it'll be updated below - for c := range choices { - if choices[c].AgendaID == d.Vote.Id { - votingPreference = choices[c].ChoiceID + for agendaID, choiceID := range choices { + if agendaID == d.Vote.Id { + votingPreference = choiceID break } } diff --git a/libwallet/assets/dcr/decodetx.go b/libwallet/assets/dcr/decodetx.go index a679d07b7..0fd4ab3f3 100644 --- a/libwallet/assets/dcr/decodetx.go +++ b/libwallet/assets/dcr/decodetx.go @@ -3,7 +3,7 @@ package dcr import ( "fmt" - w "decred.org/dcrwallet/v3/wallet" + w "decred.org/dcrwallet/v4/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/txhelper" "github.com/decred/dcrd/blockchain/stake/v5" diff --git a/libwallet/assets/dcr/message.go b/libwallet/assets/dcr/message.go index f60cd11f9..438017554 100644 --- a/libwallet/assets/dcr/message.go +++ b/libwallet/assets/dcr/message.go @@ -1,8 +1,8 @@ package dcr import ( - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/txscript/v4/stdaddr" ) diff --git a/libwallet/assets/dcr/rescan.go b/libwallet/assets/dcr/rescan.go index 0b99549d3..768d1c022 100644 --- a/libwallet/assets/dcr/rescan.go +++ b/libwallet/assets/dcr/rescan.go @@ -5,8 +5,8 @@ import ( "math" "time" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" ) diff --git a/libwallet/assets/dcr/sync.go b/libwallet/assets/dcr/sync.go index 8be6e7e6c..c672be79d 100644 --- a/libwallet/assets/dcr/sync.go +++ b/libwallet/assets/dcr/sync.go @@ -9,10 +9,10 @@ import ( "strings" "sync" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/p2p" - "decred.org/dcrwallet/v3/spv" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/p2p" + "decred.org/dcrwallet/v4/spv" + w "decred.org/dcrwallet/v4/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/addrmgr/v2" diff --git a/libwallet/assets/dcr/syncnotification.go b/libwallet/assets/dcr/syncnotification.go index 8825126ed..0d898403f 100644 --- a/libwallet/assets/dcr/syncnotification.go +++ b/libwallet/assets/dcr/syncnotification.go @@ -4,7 +4,7 @@ import ( "math" "time" - "decred.org/dcrwallet/v3/spv" + "decred.org/dcrwallet/v4/spv" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "golang.org/x/sync/errgroup" ) diff --git a/libwallet/assets/dcr/ticket.go b/libwallet/assets/dcr/ticket.go index 6fe988433..355c259f2 100644 --- a/libwallet/assets/dcr/ticket.go +++ b/libwallet/assets/dcr/ticket.go @@ -7,14 +7,15 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" - "github.com/crypto-power/cryptopower/libwallet/internal/vsp" "github.com/crypto-power/cryptopower/libwallet/utils" + "github.com/decred/dcrd/blockchain/stake/v5" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/wire" + "github.com/decred/vspd/types/v2" ) func (asset *Asset) TotalStakingRewards() (int64, error) { @@ -123,13 +124,16 @@ func (asset *Asset) PurchaseTickets(account, numTickets int32, vspHost, passphra } defer asset.LockWallet() + log.Info("Setting the ticket(s) purchasing account info") + vspClient.SetAccountInfo(account, account) + request := &w.PurchaseTicketsRequest{ Count: int(numTickets), SourceAccount: uint32(account), MinConf: asset.RequiredConfirmations(), - VSPFeeProcess: vspClient.FeePercentage, + VSPFeePercent: vspClient.FeePercentage, VSPFeePaymentProcess: func(ctx context.Context, ticketHash *chainhash.Hash, feeTx *wire.MsgTx) error { - return vspClient.Process(ctx, ticketHash, feeTx, asset.GetvspPolicy(account)) + return vspClient.Process(ctx, ticketHash, feeTx) }, } @@ -152,17 +156,6 @@ func (asset *Asset) PurchaseTickets(account, numTickets int32, vspHost, passphra return ticketsResponse.TicketHashes, err } -// GetvspPolicy creates the VSP policy using the account number provided. -// Uses the user-specified instructions for processing fee payments -// on a ticket, rather than some default policy. -func (asset *Asset) GetvspPolicy(account int32) vsp.Policy { - return vsp.Policy{ - MaxFee: 0.2e8, - FeeAcct: uint32(account), - ChangeAcct: uint32(account), - } -} - // VSPTicketInfo returns vsp-related info for a given ticket. Returns an error // if the ticket is not yet assigned to a VSP. func (asset *Asset) VSPTicketInfo(hash string) (*VSPTicketInfo, error) { @@ -204,7 +197,36 @@ func (asset *Asset) VSPTicketInfo(hash string) (*VSPTicketInfo, error) { ticketInfo.Client = vspClient - vspTicketStatus, err := vspClient.GetTicketStatus(ctx, ticketHash) + txs, _, err := asset.Internal().DCR.GetTransactionsByHashes(ctx, []*chainhash.Hash{ticketHash}) + if err != nil { + return nil, err + } + + if len(txs) == 0 { + return nil, fmt.Errorf("%v is not a ticket", ticketHash) + } + + ticketTx := txs[0] + + if len(ticketTx.TxOut) != 3 { + return nil, fmt.Errorf("ticket %v has multiple commitments", ticketHash) + } + + if !stake.IsSStx(ticketTx) { + return nil, fmt.Errorf("%v is not a ticket", ticketHash) + } + + commitmentAddr, err := stake.AddrFromSStxPkScrCommitment(ticketTx.TxOut[1].PkScript, asset.chainParams) + if err != nil { + return nil, fmt.Errorf("failed to extract commitment address from %v: %w", + ticketHash, err) + } + + req := types.TicketStatusRequest{ + TicketHash: ticketHash.String(), + } + + vspTicketStatus, err := vspClient.TicketStatus(ctx, req, commitmentAddr) if err != nil { log.Warnf("unable to get vsp ticket: %s Error: %v", hash, err) return ticketInfo, nil @@ -401,6 +423,9 @@ func (asset *Asset) runTicketBuyer(ctx context.Context, passphrase string, cfg * continue } + log.Info("Setting the ticket(s) purchasing account info") + cfg.VspClient.SetAccountInfo(cfg.PurchaseAccount, cfg.PurchaseAccount) + cancelCtx, cancel := context.WithCancel(ctx) cancels = append(cancels, cancel) buyTicket := func() { @@ -452,19 +477,14 @@ func (asset *Asset) buyTicket(ctx context.Context, passphrase string, sdiff dcru // Count is 1 to prevent combining multiple split outputs in one tx, // which can be used to link the tickets eventually purchased with the // split outputs. - vspPolicy := vsp.Policy{ - MaxFee: 0.2e8, - FeeAcct: uint32(cfg.PurchaseAccount), - ChangeAcct: uint32(cfg.PurchaseAccount), - } request := &w.PurchaseTicketsRequest{ Count: 1, SourceAccount: uint32(cfg.PurchaseAccount), Expiry: expiry, MinConf: asset.RequiredConfirmations(), - VSPFeeProcess: cfg.VspClient.FeePercentage, + VSPFeePercent: cfg.VspClient.FeePercentage, VSPFeePaymentProcess: func(ctx context.Context, ticketHash *chainhash.Hash, feeTx *wire.MsgTx) error { - return cfg.VspClient.Process(ctx, ticketHash, feeTx, vspPolicy) + return cfg.VspClient.Process(ctx, ticketHash, feeTx) }, } // Mixed split buying through CoinShuffle++, if configured. diff --git a/libwallet/assets/dcr/treasury.go b/libwallet/assets/dcr/treasury.go index 2d68ac5f0..35481d8d3 100644 --- a/libwallet/assets/dcr/treasury.go +++ b/libwallet/assets/dcr/treasury.go @@ -4,7 +4,7 @@ import ( "encoding/hex" "fmt" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/blockchain/stake/v5" diff --git a/libwallet/assets/dcr/txandblocknotifications.go b/libwallet/assets/dcr/txandblocknotifications.go index f5e046033..1b9b7246b 100644 --- a/libwallet/assets/dcr/txandblocknotifications.go +++ b/libwallet/assets/dcr/txandblocknotifications.go @@ -3,7 +3,7 @@ package dcr import ( "encoding/json" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" ) diff --git a/libwallet/assets/dcr/txauthor.go b/libwallet/assets/dcr/txauthor.go index ffe2566ba..5f7c545cc 100644 --- a/libwallet/assets/dcr/txauthor.go +++ b/libwallet/assets/dcr/txauthor.go @@ -7,11 +7,11 @@ import ( "fmt" "time" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/txauthor" - "decred.org/dcrwallet/v3/wallet/txrules" - "decred.org/dcrwallet/v3/wallet/txsizes" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" + "decred.org/dcrwallet/v4/wallet/txauthor" + "decred.org/dcrwallet/v4/wallet/txrules" + "decred.org/dcrwallet/v4/wallet/txsizes" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/txhelper" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/dcr/txindex.go b/libwallet/assets/dcr/txindex.go index ad8e98bfc..5bfb16222 100644 --- a/libwallet/assets/dcr/txindex.go +++ b/libwallet/assets/dcr/txindex.go @@ -1,7 +1,7 @@ package dcr import ( - w "decred.org/dcrwallet/v3/wallet" + w "decred.org/dcrwallet/v4/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/chaincfg/chainhash" diff --git a/libwallet/assets/dcr/txparser.go b/libwallet/assets/dcr/txparser.go index 0c0541cb0..fe8ec6137 100644 --- a/libwallet/assets/dcr/txparser.go +++ b/libwallet/assets/dcr/txparser.go @@ -3,7 +3,7 @@ package dcr import ( "fmt" - w "decred.org/dcrwallet/v3/wallet" + w "decred.org/dcrwallet/v4/wallet" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/decred/dcrd/chaincfg/chainhash" ) diff --git a/libwallet/assets/dcr/types.go b/libwallet/assets/dcr/types.go index 8519c43d3..0a051a6ec 100644 --- a/libwallet/assets/dcr/types.go +++ b/libwallet/assets/dcr/types.go @@ -5,7 +5,7 @@ import ( "fmt" "net" - "decred.org/dcrwallet/v3/wallet/udb" + "decred.org/dcrwallet/v4/wallet/udb" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/internal/vsp" "github.com/decred/dcrd/chaincfg/v3" diff --git a/libwallet/assets/dcr/vsp.go b/libwallet/assets/dcr/vsp.go index ca3a3c7a7..8ee617b6c 100644 --- a/libwallet/assets/dcr/vsp.go +++ b/libwallet/assets/dcr/vsp.go @@ -9,7 +9,7 @@ import ( "net/http" "strings" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/internal/vsp" "github.com/crypto-power/cryptopower/libwallet/utils" @@ -37,6 +37,10 @@ func (asset *Asset) VSPClient(host string, pubKey []byte) (*vsp.Client, error) { PubKey: pubKey, Dialer: nil, // optional, but consider providing a value Wallet: asset.Internal().DCR, + Params: asset.chainParams, + Policy: &vsp.Policy{ + MaxFee: 0.1e8, + }, } client, err := vsp.New(cfg) if err != nil { diff --git a/libwallet/assets/dcr/wallet.go b/libwallet/assets/dcr/wallet.go index e4991b56b..0a1687a3b 100644 --- a/libwallet/assets/dcr/wallet.go +++ b/libwallet/assets/dcr/wallet.go @@ -5,8 +5,8 @@ import ( "path/filepath" "sync" - dcrW "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/txrules" + dcrW "decred.org/dcrwallet/v4/wallet" + "decred.org/dcrwallet/v4/wallet/txrules" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/internal/loader" "github.com/crypto-power/cryptopower/libwallet/internal/loader/dcr" @@ -60,7 +60,6 @@ func initWalletLoader(chainParams *chaincfg.Params, rootdir, walletDbDriver stri stakeOptions := &dcr.StakeOptions{ VotingEnabled: false, - AddressReuse: false, VotingAddress: nil, } @@ -82,6 +81,7 @@ func initWalletLoader(chainParams *chaincfg.Params, rootdir, walletDbDriver stri ManualTickets: cfg.ManualTickets, AccountGapLimit: cfg.AccountGapLimit, MixSplitLimit: cfg.MixSplitLimit, + WatchLast: 20, // Limit number of watched addresses to 20. } walletLoader := dcr.NewLoader(loaderCfg) diff --git a/libwallet/assets/ltc/accounts.go b/libwallet/assets/ltc/accounts.go index 2f0f55e59..fbfac536d 100644 --- a/libwallet/assets/ltc/accounts.go +++ b/libwallet/assets/ltc/accounts.go @@ -7,7 +7,7 @@ import ( "strconv" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/ltcsuite/ltcd/chaincfg" diff --git a/libwallet/assets/ltc/address.go b/libwallet/assets/ltc/address.go index b18003f7d..f2263fa28 100644 --- a/libwallet/assets/ltc/address.go +++ b/libwallet/assets/ltc/address.go @@ -3,7 +3,7 @@ package ltc import ( "fmt" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/ltcsuite/ltcd/ltcutil" ) diff --git a/libwallet/assets/ltc/feerate.go b/libwallet/assets/ltc/feerate.go index 48964965a..359283350 100644 --- a/libwallet/assets/ltc/feerate.go +++ b/libwallet/assets/ltc/feerate.go @@ -7,7 +7,7 @@ import ( "strconv" "sync" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/ltcsuite/ltcd/ltcutil" diff --git a/libwallet/assets/ltc/rescan.go b/libwallet/assets/ltc/rescan.go index 6b978c742..8c10773a8 100644 --- a/libwallet/assets/ltc/rescan.go +++ b/libwallet/assets/ltc/rescan.go @@ -6,7 +6,7 @@ import ( "sync/atomic" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/ltcsuite/ltcd/ltcutil" diff --git a/libwallet/assets/ltc/sync.go b/libwallet/assets/ltc/sync.go index 31050c349..d36127185 100644 --- a/libwallet/assets/ltc/sync.go +++ b/libwallet/assets/ltc/sync.go @@ -7,7 +7,7 @@ import ( "sync/atomic" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/ltcsuite/ltcd/chaincfg" diff --git a/libwallet/assets/ltc/txandblocknotifications.go b/libwallet/assets/ltc/txandblocknotifications.go index f88397e6e..43a2c9fc7 100644 --- a/libwallet/assets/ltc/txandblocknotifications.go +++ b/libwallet/assets/ltc/txandblocknotifications.go @@ -4,7 +4,7 @@ import ( "encoding/json" "sync/atomic" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" ) diff --git a/libwallet/assets/ltc/txauthor.go b/libwallet/assets/ltc/txauthor.go index 52242dd29..ff8d0da05 100644 --- a/libwallet/assets/ltc/txauthor.go +++ b/libwallet/assets/ltc/txauthor.go @@ -8,7 +8,7 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/txhelper" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/ltc/utils.go b/libwallet/assets/ltc/utils.go index 28bce33d0..ea51faaa8 100644 --- a/libwallet/assets/ltc/utils.go +++ b/libwallet/assets/ltc/utils.go @@ -3,7 +3,7 @@ package ltc import ( "encoding/binary" - "decred.org/dcrwallet/v3/walletseed" + "decred.org/dcrwallet/v4/walletseed" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/ltcsuite/ltcd/chaincfg" diff --git a/libwallet/assets/ltc/wallet.go b/libwallet/assets/ltc/wallet.go index 84a269645..f34ddedf5 100644 --- a/libwallet/assets/ltc/wallet.go +++ b/libwallet/assets/ltc/wallet.go @@ -10,7 +10,7 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/internal/loader" "github.com/crypto-power/cryptopower/libwallet/internal/loader/ltc" diff --git a/libwallet/assets/wallet/wallet_shared.go b/libwallet/assets/wallet/wallet_shared.go index 42691fbb3..6bc55451b 100644 --- a/libwallet/assets/wallet/wallet_shared.go +++ b/libwallet/assets/wallet/wallet_shared.go @@ -9,8 +9,8 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" - w "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + w "decred.org/dcrwallet/v4/wallet" "github.com/asdine/storm" "github.com/crypto-power/cryptopower/libwallet/assets/wallet/walletdata" "github.com/crypto-power/cryptopower/libwallet/internal/loader" diff --git a/libwallet/assets/wallet/wallet_utils.go b/libwallet/assets/wallet/wallet_utils.go index 5071cbfc7..3d510c514 100644 --- a/libwallet/assets/wallet/wallet_utils.go +++ b/libwallet/assets/wallet/wallet_utils.go @@ -6,8 +6,8 @@ import ( "os" "strconv" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/walletseed" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/walletseed" "github.com/asdine/storm" btchdkeychain "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets/wallet/walletdata/save.go b/libwallet/assets/wallet/walletdata/save.go index 37de75622..3f673c869 100644 --- a/libwallet/assets/wallet/walletdata/save.go +++ b/libwallet/assets/wallet/walletdata/save.go @@ -4,7 +4,7 @@ import ( "fmt" "reflect" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" ) diff --git a/libwallet/assets_config.go b/libwallet/assets_config.go index 1133bb59e..5271f4f93 100644 --- a/libwallet/assets_config.go +++ b/libwallet/assets_config.go @@ -3,7 +3,7 @@ package libwallet import ( "fmt" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/assets_manager.go b/libwallet/assets_manager.go index b4645c82d..876ffccad 100644 --- a/libwallet/assets_manager.go +++ b/libwallet/assets_manager.go @@ -7,7 +7,7 @@ import ( "path/filepath" "strconv" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" "github.com/asdine/storm/q" "github.com/crypto-power/cryptopower/libwallet/ext" diff --git a/libwallet/badgerdb/bucket.go b/libwallet/badgerdb/bucket.go index bfa71af52..19767c634 100644 --- a/libwallet/badgerdb/bucket.go +++ b/libwallet/badgerdb/bucket.go @@ -3,7 +3,7 @@ package badgerdb import ( "bytes" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/dgraph-io/badger" ) diff --git a/libwallet/badgerdb/db.go b/libwallet/badgerdb/db.go index 1bf604884..41e1dc442 100644 --- a/libwallet/badgerdb/db.go +++ b/libwallet/badgerdb/db.go @@ -10,8 +10,8 @@ import ( "io" "os" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/wallet/walletdb" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/wallet/walletdb" "github.com/dgraph-io/badger" "github.com/dgraph-io/badger/options" ) diff --git a/libwallet/badgerdb/driver.go b/libwallet/badgerdb/driver.go index 5a2ed3eed..b693e008c 100644 --- a/libwallet/badgerdb/driver.go +++ b/libwallet/badgerdb/driver.go @@ -8,8 +8,8 @@ package badgerdb import ( "fmt" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/wallet/walletdb" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/wallet/walletdb" ) const ( diff --git a/libwallet/btc.go b/libwallet/btc.go index 567cce705..fc821423d 100644 --- a/libwallet/btc.go +++ b/libwallet/btc.go @@ -3,7 +3,7 @@ package libwallet import ( "fmt" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcwallet/waddrmgr" diff --git a/libwallet/dcr.go b/libwallet/dcr.go index d646463f8..b988360de 100644 --- a/libwallet/dcr.go +++ b/libwallet/dcr.go @@ -3,8 +3,8 @@ package libwallet import ( "context" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/walletseed" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/walletseed" "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/dcrd/hdkeychain/v3" diff --git a/libwallet/instantswap.go b/libwallet/instantswap.go index b5dee2b2f..0d94a0b40 100644 --- a/libwallet/instantswap.go +++ b/libwallet/instantswap.go @@ -5,7 +5,7 @@ import ( "math" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" api "github.com/crypto-power/instantswap/instantswap" "github.com/crypto-power/cryptopower/libwallet/assets/btc" diff --git a/libwallet/instantswap/instantswap.go b/libwallet/instantswap/instantswap.go index fccf2fc80..074e61b4e 100644 --- a/libwallet/instantswap/instantswap.go +++ b/libwallet/instantswap/instantswap.go @@ -5,7 +5,7 @@ import ( "sync" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" "github.com/asdine/storm/q" "github.com/crypto-power/instantswap/instantswap" diff --git a/libwallet/instantswap/sync.go b/libwallet/instantswap/sync.go index b0958335d..52c43198f 100644 --- a/libwallet/instantswap/sync.go +++ b/libwallet/instantswap/sync.go @@ -4,7 +4,7 @@ import ( "context" "time" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" "github.com/crypto-power/instantswap/instantswap" ) diff --git a/libwallet/internal/loader/config.go b/libwallet/internal/loader/config.go index 84f0263f8..07548a8af 100644 --- a/libwallet/internal/loader/config.go +++ b/libwallet/internal/loader/config.go @@ -5,8 +5,8 @@ import ( "os" "path/filepath" - "decred.org/dcrwallet/v3/errors" - dcrW "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + dcrW "decred.org/dcrwallet/v4/wallet" btcW "github.com/btcsuite/btcwallet/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" ltcW "github.com/ltcsuite/ltcwallet/wallet" diff --git a/libwallet/internal/loader/dcr/loader.go b/libwallet/internal/loader/dcr/loader.go index ead8ac3f1..072b996b3 100644 --- a/libwallet/internal/loader/dcr/loader.go +++ b/libwallet/internal/loader/dcr/loader.go @@ -10,15 +10,15 @@ import ( "path/filepath" "sync" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/wallet" "github.com/crypto-power/cryptopower/libwallet/internal/loader" "github.com/crypto-power/cryptopower/libwallet/utils" "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/txscript/v4/stdaddr" - _ "decred.org/dcrwallet/v3/wallet/drivers/bdb" // driver loaded during init + _ "decred.org/dcrwallet/v4/wallet/drivers/bdb" // driver loaded during init ) const walletDbName = "wallet.db" @@ -48,6 +48,7 @@ type dcrLoader struct { manualTickets bool relayFee dcrutil.Amount mixSplitLimit int + watchLast uint32 mu sync.RWMutex } @@ -55,7 +56,6 @@ type dcrLoader struct { // StakeOptions contains the various options necessary for stake mining. type StakeOptions struct { VotingEnabled bool - AddressReuse bool VotingAddress stdaddr.StakeAddress PoolAddress stdaddr.StakeAddress PoolFees float64 @@ -77,6 +77,7 @@ type LoaderConf struct { ManualTickets bool AccountGapLimit int MixSplitLimit int + WatchLast uint32 } // NewLoader constructs a DCR Loader. @@ -91,6 +92,7 @@ func NewLoader(cfg *LoaderConf) loader.AssetLoader { manualTickets: cfg.ManualTickets, relayFee: cfg.RelayFee, mixSplitLimit: cfg.MixSplitLimit, + watchLast: cfg.WatchLast, Loader: loader.NewLoader(cfg.DBDirPath), } @@ -157,7 +159,7 @@ func (l *dcrLoader) CreateWatchingOnlyWallet(ctx context.Context, params *loader DB: db, PubPassphrase: params.PubPassphrase, VotingEnabled: so.VotingEnabled, - AddressReuse: so.AddressReuse, + WatchLast: l.watchLast, VotingAddress: so.VotingAddress, PoolAddress: so.PoolAddress, PoolFees: so.PoolFees, @@ -215,7 +217,7 @@ func (l *dcrLoader) CreateNewWallet(ctx context.Context, params *loader.CreateWa DB: db, PubPassphrase: params.PubPassphrase, VotingEnabled: so.VotingEnabled, - AddressReuse: so.AddressReuse, + WatchLast: l.watchLast, VotingAddress: so.VotingAddress, PoolAddress: so.PoolAddress, PoolFees: so.PoolFees, @@ -280,7 +282,7 @@ func (l *dcrLoader) OpenExistingWallet(ctx context.Context, walletID string, pub DB: db, PubPassphrase: pubPassphrase, VotingEnabled: so.VotingEnabled, - AddressReuse: so.AddressReuse, + WatchLast: l.watchLast, VotingAddress: so.VotingAddress, PoolAddress: so.PoolAddress, PoolFees: so.PoolFees, diff --git a/libwallet/internal/politeia/errors.go b/libwallet/internal/politeia/errors.go index 1d18f7ae6..1bb1e6267 100644 --- a/libwallet/internal/politeia/errors.go +++ b/libwallet/internal/politeia/errors.go @@ -1,7 +1,7 @@ package politeia import ( - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" ) diff --git a/libwallet/internal/politeia/politeia.go b/libwallet/internal/politeia/politeia.go index 238ecb092..1ba7bd6fd 100644 --- a/libwallet/internal/politeia/politeia.go +++ b/libwallet/internal/politeia/politeia.go @@ -6,7 +6,7 @@ import ( "fmt" "sync" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" "github.com/asdine/storm/q" ) diff --git a/libwallet/internal/politeia/politeia_sync.go b/libwallet/internal/politeia/politeia_sync.go index 7c5e52344..3ff95bbb6 100644 --- a/libwallet/internal/politeia/politeia_sync.go +++ b/libwallet/internal/politeia/politeia_sync.go @@ -11,8 +11,8 @@ import ( "strconv" "time" - "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/udb" + "decred.org/dcrwallet/v4/wallet" + "decred.org/dcrwallet/v4/wallet/udb" "github.com/asdine/storm" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/txscript/v4/stdaddr" diff --git a/libwallet/internal/uniformprng/prng.go b/libwallet/internal/uniformprng/prng.go deleted file mode 100644 index 0cca7f69b..000000000 --- a/libwallet/internal/uniformprng/prng.go +++ /dev/null @@ -1,89 +0,0 @@ -// Package uniformprng implements a uniform, cryptographically secure -// pseudo-random number generator. -package uniformprng - -import ( - "encoding/binary" - "io" - "math/bits" - - "golang.org/x/crypto/chacha20" -) - -// Source returns cryptographically-secure pseudorandom numbers with uniform -// distribution. -type Source struct { - buf [8]byte - cipher *chacha20.Cipher -} - -var nonce = make([]byte, chacha20.NonceSize) - -// NewSource seeds a Source from a 32-byte key. -func NewSource(seed *[32]byte) *Source { - cipher, _ := chacha20.NewUnauthenticatedCipher(seed[:], nonce) - return &Source{cipher: cipher} -} - -// RandSource creates a Source with seed randomness read from rand. -func RandSource(rand io.Reader) (*Source, error) { - seed := new([32]byte) - _, err := io.ReadFull(rand, seed[:]) - if err != nil { - return nil, err - } - return NewSource(seed), nil -} - -// Uint32 returns a pseudo-random uint32. -func (s *Source) Uint32() uint32 { - b := s.buf[:4] - for i := range b { - b[i] = 0 - } - s.cipher.XORKeyStream(b, b) - return binary.LittleEndian.Uint32(b) -} - -// Uint32n returns a pseudo-random uint32 in range [0,n) without modulo bias. -func (s *Source) Uint32n(n uint32) uint32 { - if n < 2 { - return 0 - } - n-- - mask := ^uint32(0) >> bits.LeadingZeros32(n) - for { - u := s.Uint32() & mask - if u <= n { - return u - } - } -} - -// Int63 returns a pseudo-random 63-bit positive integer as an int64 without -// modulo bias. -func (s *Source) Int63() int64 { - b := s.buf[:] - for i := range b { - b[i] = 0 - } - s.cipher.XORKeyStream(b, b) - return int64(binary.LittleEndian.Uint64(b) &^ (1 << 63)) -} - -// Int63n returns, as an int64, a pseudo-random 63-bit positive integer in [0,n) -// without modulo bias. -// It panics if n <= 0. -func (s *Source) Int63n(n int64) int64 { - if n <= 0 { - panic("invalid argument to Int63n") - } - n-- - mask := int64(^uint64(0) >> bits.LeadingZeros64(uint64(n))) - for { - i := s.Int63() & mask - if i <= n { - return i - } - } -} diff --git a/libwallet/internal/vsp/client.go b/libwallet/internal/vsp/client.go deleted file mode 100644 index 2b3f3731c..000000000 --- a/libwallet/internal/vsp/client.go +++ /dev/null @@ -1,105 +0,0 @@ -package vsp - -import ( - "context" - "crypto/ed25519" - "encoding/base64" - "encoding/json" - "fmt" - "net/http" - - "github.com/crypto-power/cryptopower/libwallet/utils" - "github.com/decred/dcrd/txscript/v4/stdaddr" -) - -type client struct { - http.Client - url string - pub []byte - sign func(context.Context, string, stdaddr.Address) ([]byte, error) -} - -type signer interface { - SignMessage(ctx context.Context, message string, address stdaddr.Address) ([]byte, error) -} - -func newClient(url string, pub []byte, s signer) *client { - return &client{url: url, pub: pub, sign: s.SignMessage} -} - -type BadRequestError struct { - HTTPStatus int `json:"-"` - Code int `json:"code"` - Message string `json:"message"` -} - -func (e *BadRequestError) Error() string { return e.Message } - -func (c *client) post(ctx context.Context, path string, addr stdaddr.Address, response interface{}, body []byte) error { - return c.do(ctx, http.MethodPost, path, addr, response, body) -} - -func (c *client) get(ctx context.Context, path string, resp interface{}) error { - return c.do(ctx, http.MethodGet, path, nil, resp, nil) -} - -func (c *client) do(ctx context.Context, method, path string, addr stdaddr.Address, response interface{}, body []byte) error { - var err error - var sig []byte - reqConf := &utils.ReqConfig{ - Method: method, - HTTPURL: c.url + path, - IsRetByte: true, - Headers: make(http.Header), - } - - if method == http.MethodPost { - sig, err = c.sign(ctx, string(body), addr) - if err != nil { - return fmt.Errorf("sign request: %w", err) - } - reqConf.Payload = body - } - - // Add cookies. - if sig != nil { - reqConf.Headers.Add("VSP-Client-Signature", base64.StdEncoding.EncodeToString(sig)) - } - - respBytes := []byte{} - reply, err := utils.HTTPRequest(reqConf, &respBytes) - if err != nil && reply == nil { - // Status code errors are handled below. - return err - } - - status := reply.StatusCode - is200 := status == 200 - is4xx := status >= 400 && status <= 499 - if !(is200 || is4xx) { - return err - } - - if err = json.Unmarshal(respBytes, response); err != nil { - return fmt.Errorf("could not pack response data: %w", err) - } - - sigBase64 := reply.Header.Get("VSP-Server-Signature") - sig, err = base64.StdEncoding.DecodeString(sigBase64) - if err != nil { - return fmt.Errorf("cannot authenticate server: %w", err) - } - - if !ed25519.Verify(c.pub, respBytes, sig) { - return fmt.Errorf("cannot authenticate server: invalid signature") - } - - var apiError *BadRequestError - if is4xx { - apiError = new(BadRequestError) - apiError.HTTPStatus = status - return apiError - } - - return nil -} diff --git a/libwallet/internal/vsp/errors.go b/libwallet/internal/vsp/errors.go deleted file mode 100644 index 64c2e13b6..000000000 --- a/libwallet/internal/vsp/errors.go +++ /dev/null @@ -1,21 +0,0 @@ -package vsp - -const ( - codeBadRequest = iota - codeInternalErr - codeVspClosed - codeFeeAlreadyReceived - codeInvalidFeeTx - codeFeeTooSmall - codeUnknownTicket - codeTicketCannotVote - codeFeeExpired - codeInvalidVoteChoices - codeBadSignature - codeInvalidPrivKey - codeFeeNotReceived - codeInvalidTicket - codeCannotBroadcastTicket - codeCannotBroadcastFee - codeCannotBroadcastFeeUnknownOutputs -) diff --git a/libwallet/internal/vsp/feepayment.go b/libwallet/internal/vsp/feepayment.go index c95cf7bac..f7d54bd85 100644 --- a/libwallet/internal/vsp/feepayment.go +++ b/libwallet/internal/vsp/feepayment.go @@ -3,60 +3,57 @@ package vsp import ( "bytes" "context" - cryptorand "crypto/rand" + "crypto/rand" + "encoding/binary" "encoding/hex" - "encoding/json" "fmt" + "math/bits" "sync" "time" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/txrules" - "decred.org/dcrwallet/v3/wallet/txsizes" - "github.com/crypto-power/cryptopower/libwallet/internal/uniformprng" + "decred.org/dcrwallet/v4/errors" "github.com/decred/dcrd/blockchain/stake/v5" "github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/dcrd/dcrutil/v4" - "github.com/decred/dcrd/txscript/v4" "github.com/decred/dcrd/txscript/v4/stdaddr" "github.com/decred/dcrd/txscript/v4/stdscript" "github.com/decred/dcrd/wire" + "github.com/decred/vspd/types/v2" ) -var prng lockedRand - -type lockedRand struct { - mu sync.Mutex - rand *uniformprng.Source -} - -func (r *lockedRand) int63n(n int64) int64 { - r.mu.Lock() - defer r.mu.Unlock() - return r.rand.Int63n(n) -} - -// duration returns a random time.Duration in [0,d) with uniform distribution. -func (r *lockedRand) duration(d time.Duration) time.Duration { - return time.Duration(r.int63n(int64(d))) +// randInt63 returns a cryptographically random 63-bit positive integer as an +// int64. +func randInt63() int64 { + buf := make([]byte, 8) + _, err := rand.Read(buf) + if err != nil { + panic(fmt.Sprintf("unhandled crypto/rand error: %v", err)) + } + return int64(binary.LittleEndian.Uint64(buf) &^ (1 << 63)) } -func (r *lockedRand) coinflip() bool { - r.mu.Lock() - defer r.mu.Unlock() - return r.rand.Uint32n(2) == 0 +// randInt63n returns, as an int64, a cryptographically-random 63-bit positive +// integer in [0,n) without modulo bias. +// It panics if n <= 0. +func randInt63n(n int64) int64 { + if n <= 0 { + panic("invalid argument to randInt63n") + } + n-- + mask := int64(^uint64(0) >> bits.LeadingZeros64(uint64(n))) + for { + v := randInt63() & mask + if v <= n { + return v + } + } } -func init() { - source, err := uniformprng.RandSource(cryptorand.Reader) - if err != nil { - panic(err) - } - prng = lockedRand{ - rand: source, - } +// randomDuration returns a random time.Duration in [0,d) with uniform +// distribution. +func randomDuration(d time.Duration) time.Duration { + return time.Duration(randInt63n(int64(d))) } var ( @@ -81,7 +78,7 @@ type feePayment struct { ticketHash chainhash.Hash commitmentAddr stdaddr.StakeAddress votingAddr stdaddr.StakeAddress - policy Policy + policy *Policy // Requires locking for all access outside of Client.feePayment mu sync.Mutex @@ -97,16 +94,18 @@ type feePayment struct { timerMu sync.Mutex timer *time.Timer + + params *chaincfg.Params } type state uint32 const ( _ state = iota - unprocessed - feePublished + Unprocessed + FeePublished _ // ... - ticketSpent + TicketSpent ) func parseTicket(ticket *wire.MsgTx, params *chaincfg.Params) ( @@ -135,20 +134,61 @@ func parseTicket(ticket *wire.MsgTx, params *chaincfg.Params) ( return } +// calcHeights checks if the ticket has been mined, and if so, sets the live +// height and expiry height fields. Should be called with mutex already held. +func (fp *feePayment) calcHeights() { + _, minedHeight, err := fp.client.wallet.TxBlock(fp.ctx, &fp.ticketHash) + if err != nil { + // This is not expected to ever error, as the ticket has already been + // fetched from the wallet at least one before this point is reached. + log.Errorf("Failed to query block which mines ticket: %v", err) + return + } + + if minedHeight < 2 { + return + } + + // Note the off-by-one; this is correct. Tickets become live one block after + // the params would indicate. + fp.ticketLive = minedHeight + int32(fp.params.TicketMaturity) + 1 + fp.ticketExpires = fp.ticketLive + int32(fp.params.TicketExpiry) +} + +// expiryHeight returns the height at which the ticket expires. Returns zero if +// the block is not yet mined. Should be called with mutex already held. +func (fp *feePayment) expiryHeight() int32 { + if fp.ticketExpires == 0 { + fp.calcHeights() + } + + return fp.ticketExpires +} + +// liveHeight returns the height at which the ticket becomes live. Returns zero +// if the block is not yet mined. Should be called with mutex already held. +func (fp *feePayment) liveHeight() int32 { + if fp.ticketLive == 0 { + fp.calcHeights() + } + + return fp.ticketLive +} + func (fp *feePayment) ticketSpent() bool { ctx := fp.ctx ticketOut := wire.OutPoint{Hash: fp.ticketHash, Index: 0, Tree: 1} - _, _, err := fp.client.Wallet.Spender(ctx, &ticketOut) + _, _, err := fp.client.wallet.Spender(ctx, &ticketOut) return err == nil } func (fp *feePayment) ticketExpired() bool { ctx := fp.ctx - w := fp.client.Wallet + w := fp.client.wallet _, tipHeight := w.MainChainTip(ctx) fp.mu.Lock() - expires := fp.ticketExpires + expires := fp.expiryHeight() fp.mu.Unlock() return expires > 0 && tipHeight >= expires @@ -180,7 +220,7 @@ func (fp *feePayment) remove(reason string) { // feePayment returns an existing managed fee payment, or creates and begins // processing a fee payment for a ticket. -func (c *Client) feePayment(ticketHash *chainhash.Hash, policy Policy, paidConfirmed bool) (fp *feePayment) { +func (c *Client) feePayment(ctx context.Context, ticketHash *chainhash.Hash, paidConfirmed bool) (fp *feePayment) { c.mu.Lock() fp = c.jobs[*ticketHash] c.mu.Unlock() @@ -208,20 +248,19 @@ func (c *Client) feePayment(ticketHash *chainhash.Hash, policy Policy, paidConfi } }() - ctx := context.Background() - w := c.Wallet - params := w.ChainParams() + w := c.wallet fp = &feePayment{ client: c, - ctx: ctx, + ctx: context.Background(), ticketHash: *ticketHash, - policy: policy, + policy: c.policy, + params: c.params, } // No VSP interaction is required for spent tickets. if fp.ticketSpent() { - fp.state = ticketSpent + fp.state = TicketSpent return fp } @@ -231,29 +270,14 @@ func (c *Client) feePayment(ticketHash *chainhash.Hash, policy Policy, paidConfi return nil } - _, ticketHeight, err := w.TxBlock(ctx, ticketHash) - if err != nil { - // This is not expected to ever error, as the ticket was fetched - // from the wallet in the above call. - log.Errorf("failed to query block which mines ticket: %v", err) - return nil - } - if ticketHeight >= 2 { - // Note the off-by-one; this is correct. Tickets become live - // one block after the params would indicate. - fp.ticketLive = ticketHeight + int32(params.TicketMaturity) + 1 - fp.ticketExpires = fp.ticketLive + int32(params.TicketExpiry) - } - - fp.votingAddr, fp.commitmentAddr, err = parseTicket(ticket, params) + fp.votingAddr, fp.commitmentAddr, err = parseTicket(ticket, c.params) if err != nil { log.Errorf("%v is not a ticket: %v", ticketHash, err) return nil } - // Try to access the voting key, ignore error unless the wallet is - // locked. + // Try to access the voting key. fp.votingKey, err = w.DumpWIFPrivateKey(ctx, fp.votingAddr) - if err != nil && !errors.Is(errors.Locked, err) { + if err != nil { log.Errorf("no voting key for ticket %v: %v", ticketHash, err) return nil } @@ -282,19 +306,19 @@ func (c *Client) feePayment(ticketHash *chainhash.Hash, policy Policy, paidConfi // If database has been updated to paid or confirmed status, we can forgo // this step. if !paidConfirmed { - err = w.UpdateVspTicketFeeToStarted(ctx, ticketHash, &feeHash, c.client.url, c.client.pub) + err = w.UpdateVspTicketFeeToStarted(ctx, ticketHash, &feeHash, c.Client.URL, c.Client.PubKey) if err != nil { return fp } - fp.state = unprocessed // XXX fee created, but perhaps not submitted with vsp. + fp.state = Unprocessed // XXX fee created, but perhaps not submitted with vsp. fp.fee = -1 // XXX fee amount (not needed anymore?) } return fp } func (c *Client) tx(ctx context.Context, hash *chainhash.Hash) (*wire.MsgTx, error) { - txs, _, err := c.Wallet.GetTransactionsByHashes(ctx, []*chainhash.Hash{hash}) + txs, _, err := c.wallet.GetTransactionsByHashes(ctx, []*chainhash.Hash{hash}) if err != nil { return nil, err } @@ -322,20 +346,19 @@ func (fp *feePayment) schedule(name string, method func() error) { } func (fp *feePayment) next() time.Duration { - w := fp.client.Wallet - params := w.ChainParams() + w := fp.client.wallet _, tipHeight := w.MainChainTip(fp.ctx) fp.mu.Lock() - ticketLive := fp.ticketLive - ticketExpires := fp.ticketExpires + ticketLive := fp.liveHeight() + ticketExpires := fp.expiryHeight() fp.mu.Unlock() var jitter time.Duration switch { case tipHeight < ticketLive: // immature, mined ticket - blocksUntilLive := ticketExpires - tipHeight - jitter = params.TargetTimePerBlock * time.Duration(blocksUntilLive) + blocksUntilLive := ticketLive - tipHeight + jitter = fp.params.TargetTimePerBlock * time.Duration(blocksUntilLive) if jitter > immatureJitter { jitter = immatureJitter } @@ -345,7 +368,7 @@ func (fp *feePayment) next() time.Duration { jitter = unminedJitter } - return prng.duration(jitter) + return randomDuration(jitter) } // task returns a function running a feePayment method. @@ -369,8 +392,6 @@ func (fp *feePayment) stop() { func (fp *feePayment) receiveFeeAddress() error { ctx := fp.ctx - w := fp.client.Wallet - params := w.ChainParams() // stop processing if ticket is expired or spent if fp.removedExpiredOrSpent() { @@ -391,40 +412,29 @@ func (fp *feePayment) receiveFeeAddress() error { parentHash, err) } - var response struct { - Timestamp int64 `json:"timestamp"` - FeeAddress string `json:"feeaddress"` - FeeAmount int64 `json:"feeamount"` - Request []byte `json:"request"` - } - requestBody, err := json.Marshal(&struct { - Timestamp int64 `json:"timestamp"` - TicketHash string `json:"tickethash"` - TicketHex json.Marshaler `json:"tickethex"` - ParentHex json.Marshaler `json:"parenthex"` - }{ - Timestamp: time.Now().Unix(), - TicketHash: fp.ticketHash.String(), - TicketHex: txMarshaler(ticket), - ParentHex: txMarshaler(parent), - }) + ticketHex, err := marshalTx(ticket) if err != nil { return err } - err = fp.client.post(ctx, "/api/v3/feeaddress", fp.commitmentAddr, &response, - json.RawMessage(requestBody)) + parentHex, err := marshalTx(parent) if err != nil { return err } - // verify initial request matches server - if !bytes.Equal(requestBody, response.Request) { - return fmt.Errorf("server response has differing request: %#v != %#v", - requestBody, response.Request) + req := types.FeeAddressRequest{ + Timestamp: time.Now().Unix(), + TicketHash: fp.ticketHash.String(), + TicketHex: ticketHex, + ParentHex: parentHex, } - feeAmount := dcrutil.Amount(response.FeeAmount) - feeAddr, err := stdaddr.DecodeAddress(response.FeeAddress, params) + resp, err := fp.client.FeeAddress(ctx, req, fp.commitmentAddr) + if err != nil { + return err + } + + feeAmount := dcrutil.Amount(resp.FeeAmount) + feeAddr, err := stdaddr.DecodeAddress(resp.FeeAddress, fp.params) if err != nil { return fmt.Errorf("server fee address invalid: %w", err) } @@ -453,7 +463,7 @@ func (fp *feePayment) receiveFeeAddress() error { // be dereferenced. func (fp *feePayment) makeFeeTx(tx *wire.MsgTx) error { ctx := fp.ctx - w := fp.client.Wallet + w := fp.client.wallet fp.mu.Lock() fee := fp.fee @@ -494,104 +504,13 @@ func (fp *feePayment) makeFeeTx(tx *wire.MsgTx) error { fp.mu.Unlock() } - // Reserve new outputs to pay the fee if outputs have not already been - // reserved. This will the the case for fee payments that were begun on - // already purchased tickets, where the caller did not ensure that fee - // outputs would already be reserved. - if len(tx.TxIn) == 0 { - const minconf = 1 - inputs, err := w.ReserveOutputsForAmount(ctx, fp.policy.FeeAcct, fee, minconf) - if err != nil { - return fmt.Errorf("unable to reserve enough output value to "+ - "pay VSP fee for ticket %v: %w", fp.ticketHash, err) - } - for _, in := range inputs { - tx.AddTxIn(wire.NewTxIn(&in.OutPoint, in.PrevOut.Value, nil)) - } - // The transaction will be added to the wallet in an unpublished - // state, so there is no need to leave the outputs locked. - defer func() { - for _, in := range inputs { - w.UnlockOutpoint(&in.OutPoint.Hash, in.OutPoint.Index) - } - }() - } - - var input int64 - for _, in := range tx.TxIn { - input += in.ValueIn - } - if input < int64(fee) { - err := fmt.Errorf("not enough input value to pay fee: %v < %v", - dcrutil.Amount(input), fee) - return err - } - - vers, feeScript := feeAddr.PaymentScript() - - addr, err := w.NewChangeAddress(ctx, fp.policy.ChangeAcct) + err := w.CreateVspPayment(ctx, tx, fee, feeAddr, fp.policy.FeeAcct, fp.policy.ChangeAcct) if err != nil { - log.Warnf("failed to get new change address: %v", err) - return err - } - var changeOut *wire.TxOut - switch addr := addr.(type) { - case wallet.Address: - vers, script := addr.PaymentScript() - changeOut = &wire.TxOut{PkScript: script, Version: vers} - default: - return fmt.Errorf("failed to convert '%T' to wallet.Address", addr) - } - - tx.TxOut = append(tx.TxOut[:0], &wire.TxOut{ - Value: int64(fee), - Version: vers, - PkScript: feeScript, - }) - feeRate := w.RelayFee() - scriptSizes := make([]int, len(tx.TxIn)) - for i := range scriptSizes { - scriptSizes[i] = txsizes.RedeemP2PKHSigScriptSize - } - est := txsizes.EstimateSerializeSize(scriptSizes, tx.TxOut, txsizes.P2PKHPkScriptSize) - change := input - change -= tx.TxOut[0].Value - change -= int64(txrules.FeeForSerializeSize(feeRate, est)) - if !txrules.IsDustAmount(dcrutil.Amount(change), txsizes.P2PKHPkScriptSize, feeRate) { - changeOut.Value = change - tx.TxOut = append(tx.TxOut, changeOut) - // randomize position - if prng.coinflip() { - tx.TxOut[0], tx.TxOut[1] = tx.TxOut[1], tx.TxOut[0] - } + return fmt.Errorf("unable to create VSP fee tx for ticket %v: %w", fp.ticketHash, err) } feeHash := tx.TxHash() - - // sign - sigErrs, err := w.SignTransaction(ctx, tx, txscript.SigHashAll, nil, nil, nil) - if err != nil || len(sigErrs) > 0 { - log.Errorf("failed to sign transaction: %v", err) - sigErrStr := "" - for _, sigErr := range sigErrs { - log.Errorf("\t%v", sigErr) - sigErrStr = fmt.Sprintf("\t%v", sigErr) + " " - } - if err != nil { - return err - } - return fmt.Errorf(sigErrStr) - } - - err = w.SetPublished(ctx, &feeHash, false) - if err != nil { - return err - } - err = w.AddTransaction(ctx, tx, nil) - if err != nil { - return err - } - err = w.UpdateVspTicketFeeToPaid(ctx, &fp.ticketHash, &feeHash, fp.client.url, fp.client.pub) + err = w.UpdateVspTicketFeeToPaid(ctx, &fp.ticketHash, &feeHash, fp.client.URL, fp.client.PubKey) if err != nil { return err } @@ -605,27 +524,7 @@ func (fp *feePayment) makeFeeTx(tx *wire.MsgTx) error { return nil } -type TicketStatus struct { - Timestamp int64 `json:"timestamp"` - TicketConfirmed bool `json:"ticketconfirmed"` - FeeTxStatus string `json:"feetxstatus"` - FeeTxHash string `json:"feetxhash"` - VoteChoices map[string]string `json:"votechoices"` - TSpendPolicy map[string]string `json:"tspendpolicy"` - TreasuryPolicy map[string]string `json:"treasurypolicy"` - Request []byte `json:"request"` -} - -// GetTicketStatus calls the VSP's TicketStatus API for the provided ticket hash -// and returns the VSP's response. -func (c *Client) GetTicketStatus(ctx context.Context, ticketHash *chainhash.Hash) (*TicketStatus, error) { - return c.status(ctx, ticketHash) -} - -func (c *Client) status(ctx context.Context, ticketHash *chainhash.Hash) (*TicketStatus, error) { - w := c.Wallet - params := w.ChainParams() - +func (c *Client) status(ctx context.Context, ticketHash *chainhash.Hash) (*types.TicketStatusResponse, error) { ticketTx, err := c.tx(ctx, ticketHash) if err != nil { return nil, fmt.Errorf("failed to retrieve ticket %v: %w", ticketHash, err) @@ -637,45 +536,29 @@ func (c *Client) status(ctx context.Context, ticketHash *chainhash.Hash) (*Ticke if !stake.IsSStx(ticketTx) { return nil, fmt.Errorf("%v is not a ticket", ticketHash) } - commitmentAddr, err := stake.AddrFromSStxPkScrCommitment(ticketTx.TxOut[1].PkScript, params) + commitmentAddr, err := stake.AddrFromSStxPkScrCommitment(ticketTx.TxOut[1].PkScript, c.params) if err != nil { return nil, fmt.Errorf("failed to extract commitment address from %v: %w", ticketHash, err) } - var resp TicketStatus - requestBody, err := json.Marshal(&struct { - TicketHash string `json:"tickethash"` - }{ + req := types.TicketStatusRequest{ TicketHash: ticketHash.String(), - }) - if err != nil { - return nil, err } - err = c.post(ctx, "/api/v3/ticketstatus", commitmentAddr, &resp, - json.RawMessage(requestBody)) + + resp, err := c.Client.TicketStatus(ctx, req, commitmentAddr) if err != nil { return nil, err } - // verify initial request matches server - if !bytes.Equal(requestBody, resp.Request) { - log.Warnf("server response has differing request: %#v != %#v", - requestBody, resp.Request) - return nil, fmt.Errorf("server response contains differing request") - } - // XXX validate server timestamp? - return &resp, nil + return resp, nil } func (c *Client) setVoteChoices(ctx context.Context, ticketHash *chainhash.Hash, - choices []wallet.AgendaChoice, tspendPolicy map[string]string, treasuryPolicy map[string]string, + choices map[string]string, tspendPolicy map[string]string, treasuryPolicy map[string]string, ) error { - w := c.Wallet - params := w.ChainParams() - ticketTx, err := c.tx(ctx, ticketHash) if err != nil { return fmt.Errorf("failed to retrieve ticket %v: %w", ticketHash, err) @@ -688,50 +571,25 @@ func (c *Client) setVoteChoices(ctx context.Context, ticketHash *chainhash.Hash, return fmt.Errorf("ticket %v has multiple commitments: %w", ticketHash, errNotSolo) } - commitmentAddr, err := stake.AddrFromSStxPkScrCommitment(ticketTx.TxOut[1].PkScript, params) + commitmentAddr, err := stake.AddrFromSStxPkScrCommitment(ticketTx.TxOut[1].PkScript, c.params) if err != nil { return fmt.Errorf("failed to extract commitment address from %v: %w", ticketHash, err) } - agendaChoices := make(map[string]string, len(choices)) - - // Prepare agenda choice - for _, c := range choices { - agendaChoices[c.AgendaID] = c.ChoiceID - } - - var resp TicketStatus - requestBody, err := json.Marshal(&struct { - Timestamp int64 `json:"timestamp"` - TicketHash string `json:"tickethash"` - VoteChoices map[string]string `json:"votechoices"` - TSpendPolicy map[string]string `json:"tspendpolicy"` - TreasuryPolicy map[string]string `json:"treasurypolicy"` - }{ + req := types.SetVoteChoicesRequest{ Timestamp: time.Now().Unix(), TicketHash: ticketHash.String(), - VoteChoices: agendaChoices, + VoteChoices: choices, TSpendPolicy: tspendPolicy, TreasuryPolicy: treasuryPolicy, - }) - if err != nil { - return err } - err = c.post(ctx, "/api/v3/setvotechoices", commitmentAddr, &resp, - json.RawMessage(requestBody)) + _, err = c.Client.SetVoteChoices(ctx, req, commitmentAddr) if err != nil { return err } - // verify initial request matches server - if !bytes.Equal(requestBody, resp.Request) { - log.Warnf("server response has differing request: %#v != %#v", - requestBody, resp.Request) - return fmt.Errorf("server response contains differing request") - } - // XXX validate server timestamp? return nil @@ -739,7 +597,7 @@ func (c *Client) setVoteChoices(ctx context.Context, ticketHash *chainhash.Hash, func (fp *feePayment) reconcilePayment() error { ctx := fp.ctx - w := fp.client.Wallet + w := fp.client.wallet // stop processing if ticket is expired or spent // XXX if ticket is no longer saved by wallet (because the tx expired, @@ -760,10 +618,9 @@ func (fp *feePayment) reconcilePayment() error { if feeTx == nil || len(feeTx.TxOut) == 0 { err := fp.makeFeeTx(nil) if err != nil { - var apiErr *BadRequestError - if errors.As(err, &apiErr) && apiErr.Code == codeTicketCannotVote { + var apiErr types.ErrorResponse + if errors.As(err, &apiErr) && apiErr.Code == types.ErrTicketCannotVote { fp.remove("ticket cannot vote") - // Tickets will be automatically revoked. } return err } @@ -784,21 +641,21 @@ func (fp *feePayment) reconcilePayment() error { fp.mu.Lock() feeHash := fp.feeHash fp.mu.Unlock() - var apiErr *BadRequestError + var apiErr types.ErrorResponse if errors.As(err, &apiErr) { switch apiErr.Code { - case codeFeeAlreadyReceived: + case types.ErrFeeAlreadyReceived: err = w.SetPublished(ctx, &feeHash, true) if err != nil { return err } - err = w.UpdateVspTicketFeeToPaid(ctx, &fp.ticketHash, &feeHash, fp.client.url, fp.client.pub) + err = w.UpdateVspTicketFeeToPaid(ctx, &fp.ticketHash, &feeHash, fp.client.URL, fp.client.PubKey) if err != nil { return err } err = nil - case codeInvalidFeeTx, codeCannotBroadcastFee: - err := w.UpdateVspTicketFeeToErrored(ctx, &fp.ticketHash, fp.client.url, fp.client.pub) + case types.ErrInvalidFeeTx, types.ErrCannotBroadcastFee: + err := w.UpdateVspTicketFeeToErrored(ctx, &fp.ticketHash, fp.client.URL, fp.client.PubKey) if err != nil { return err } @@ -816,15 +673,11 @@ func (fp *feePayment) reconcilePayment() error { return err } - err = w.UpdateVspTicketFeeToPaid(ctx, &fp.ticketHash, &feeHash, fp.client.url, fp.client.pub) + err = w.UpdateVspTicketFeeToPaid(ctx, &fp.ticketHash, &feeHash, fp.client.URL, fp.client.PubKey) if err != nil { return err } - // confirmPayment will remove the fee payment processing when the fee - // has reached sufficient confirmations, and reschedule itself if the - // fee is not confirmed yet. If the fee tx is ever removed from the - // wallet, this will schedule another reconcile. return fp.confirmPayment() /* @@ -837,7 +690,7 @@ func (fp *feePayment) reconcilePayment() error { func (fp *feePayment) submitPayment() (err error) { ctx := fp.ctx - w := fp.client.Wallet + w := fp.client.wallet // stop processing if ticket is expired or spent if fp.removedExpiredOrSpent() { @@ -870,44 +723,30 @@ func (fp *feePayment) submitPayment() (err error) { } // Retrieve voting preferences - voteChoices := make(map[string]string) - agendaChoices, _, err := w.AgendaChoices(ctx, &fp.ticketHash) + voteChoices, _, err := w.AgendaChoices(ctx, &fp.ticketHash) if err != nil { return err } - for _, agendaChoice := range agendaChoices { - voteChoices[agendaChoice.AgendaID] = agendaChoice.ChoiceID - } - var payfeeResponse struct { - Timestamp int64 `json:"timestamp"` - Request []byte `json:"request"` + feeTxHex, err := marshalTx(feeTx) + if err != nil { + return err } - requestBody, err := json.Marshal(&struct { - Timestamp int64 `json:"timestamp"` - TicketHash string `json:"tickethash"` - FeeTx json.Marshaler `json:"feetx"` - VotingKey string `json:"votingkey"` - VoteChoices map[string]string `json:"votechoices"` - TSpendPolicy map[string]string `json:"tspendpolicy"` - TreasuryPolicy map[string]string `json:"treasurypolicy"` - }{ + + req := types.PayFeeRequest{ Timestamp: time.Now().Unix(), TicketHash: fp.ticketHash.String(), - FeeTx: txMarshaler(feeTx), + FeeTx: feeTxHex, VotingKey: votingKey, VoteChoices: voteChoices, TSpendPolicy: w.TSpendPolicyForTicket(&fp.ticketHash), TreasuryPolicy: w.TreasuryKeyPolicyForTicket(&fp.ticketHash), - }) - if err != nil { - return err } - err = fp.client.post(ctx, "/api/v3/payfee", fp.commitmentAddr, - &payfeeResponse, json.RawMessage(requestBody)) + + _, err = fp.client.PayFee(ctx, req, fp.commitmentAddr) if err != nil { - var apiErr *BadRequestError - if errors.As(err, &apiErr) && apiErr.Code == codeFeeExpired { + var apiErr types.ErrorResponse + if errors.As(err, &apiErr) && apiErr.Code == types.ErrFeeExpired { // Fee has been expired, so abandon current feetx, set fp.feeTx // to nil and retry submit payment to make a new fee tx. feeHash := feeTx.TxHash() @@ -915,27 +754,26 @@ func (fp *feePayment) submitPayment() (err error) { if err != nil { log.Errorf("error abandoning expired fee tx %v", err) } + fp.mu.Lock() fp.feeTx = nil + fp.mu.Unlock() } return fmt.Errorf("payfee: %w", err) } - // Check for matching original request. - // This is signed by the VSP, and the signature - // has already been checked above. - if !bytes.Equal(requestBody, payfeeResponse.Request) { - return fmt.Errorf("server response has differing request: %#v != %#v", - requestBody, payfeeResponse.Request) - } // TODO - validate server timestamp? log.Infof("successfully processed %v", fp.ticketHash) return nil } +// confirmPayment will remove the fee payment processing when the fee has +// reached sufficient confirmations, and reschedule itself if the fee is not +// confirmed yet. If the fee tx is ever removed from the wallet, this will +// schedule another reconcile. func (fp *feePayment) confirmPayment() (err error) { ctx := fp.ctx - w := fp.client.Wallet + w := fp.client.wallet // stop processing if ticket is expired or spent if fp.removedExpiredOrSpent() { @@ -950,33 +788,8 @@ func (fp *feePayment) confirmPayment() (err error) { }() status, err := fp.client.status(ctx, &fp.ticketHash) - // Suppress log if the wallet is currently locked. - if err != nil && !errors.Is(err, errors.Locked) { - log.Warnf("Rescheduling status check for %v: %v", &fp.ticketHash, err) - } if err != nil { - // Stop processing if the status check cannot be performed, but - // a significant amount of confirmations are observed on the fee - // transaction. - // - // Otherwise, chedule another confirmation check, in case the - // status API can be performed at a later time or more - // confirmations are observed. - fp.mu.Lock() - feeHash := fp.feeHash - fp.mu.Unlock() - confs, err := w.TxConfirms(ctx, &feeHash) - if err != nil { - return err - } - if confs >= 6 { - fp.remove("confirmed") - err = w.UpdateVspTicketFeeToConfirmed(ctx, &fp.ticketHash, &feeHash, fp.client.url, fp.client.pub) - if err != nil { - return err - } - return nil - } + log.Warnf("Rescheduling status check for %v: %v", &fp.ticketHash, err) fp.schedule("confirm payment", fp.confirmPayment) return nil } @@ -995,7 +808,10 @@ func (fp *feePayment) confirmPayment() (err error) { case "confirmed": fp.remove("confirmed by VSP") // nothing scheduled - err = w.UpdateVspTicketFeeToConfirmed(ctx, &fp.ticketHash, &fp.feeHash, fp.client.url, fp.client.pub) + fp.mu.Lock() + feeHash := fp.feeHash + fp.mu.Unlock() + err = w.UpdateVspTicketFeeToConfirmed(ctx, &fp.ticketHash, &feeHash, fp.client.URL, fp.client.PubKey) if err != nil { return err } @@ -1007,27 +823,16 @@ func (fp *feePayment) confirmPayment() (err error) { return nil default: // XXX put in unknown state - log.Warnf("VSP responded with %v for %v", status.FeeTxStatus, - &fp.ticketHash) + log.Warnf("VSP responded with unknown FeeTxStatus %q for %v", + status.FeeTxStatus, &fp.ticketHash) } return nil } -type marshaler struct { - marshaled []byte - err error -} - -func (m *marshaler) MarshalJSON() ([]byte, error) { - return m.marshaled, m.err -} - -func txMarshaler(tx *wire.MsgTx) json.Marshaler { +func marshalTx(tx *wire.MsgTx) (string, error) { var buf bytes.Buffer - buf.Grow(2 + tx.SerializeSize()*2) - buf.WriteByte('"') + buf.Grow(tx.SerializeSize() * 2) err := tx.Serialize(hex.NewEncoder(&buf)) - buf.WriteByte('"') - return &marshaler{buf.Bytes(), err} + return buf.String(), err } diff --git a/libwallet/internal/vsp/vsp.go b/libwallet/internal/vsp/vsp.go index d5fb4afe8..a3b05741a 100644 --- a/libwallet/internal/vsp/vsp.go +++ b/libwallet/internal/vsp/vsp.go @@ -8,13 +8,15 @@ import ( "net/url" "sync" - "decred.org/dcrwallet/v3/errors" - "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/udb" + "decred.org/dcrwallet/v4/errors" + "decred.org/dcrwallet/v4/wallet" + "decred.org/dcrwallet/v4/wallet/udb" "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/txscript/v4/stdaddr" "github.com/decred/dcrd/wire" + vspd "github.com/decred/vspd/client/v2" ) type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error) @@ -26,11 +28,13 @@ type Policy struct { } type Client struct { - Wallet *wallet.Wallet - *client + wallet *wallet.Wallet + policy *Policy + *vspd.Client - mu sync.Mutex - jobs map[chainhash.Hash]*feePayment + mu sync.Mutex + jobs map[chainhash.Hash]*feePayment + params *chaincfg.Params } type Config struct { @@ -45,6 +49,11 @@ type Config struct { // Wallet specifies a loaded wallet. Wallet *wallet.Wallet + // Default policy for fee payments unless another is provided by the + // caller. + Policy *Policy + + Params *chaincfg.Params } func New(cfg Config) (*Client, error) { @@ -53,111 +62,99 @@ func New(cfg Config) (*Client, error) { return nil, err } + if len(cfg.PubKey) == 0 { + return nil, errors.New("pubkey option not set") + } + if cfg.Wallet == nil { - return nil, fmt.Errorf("wallet option not set") + return nil, errors.New("wallet option not set") + } + + if cfg.Params == nil { + return nil, errors.New("params option not set") } - client := newClient(u.String(), cfg.PubKey, cfg.Wallet) + if cfg.Policy == nil { + return nil, errors.New("policy option not set") + } + + client := &vspd.Client{ + URL: u.String(), + PubKey: cfg.PubKey, + Sign: cfg.Wallet.SignMessage, + Log: log, + } client.Transport = &http.Transport{ DialContext: cfg.Dialer, } v := &Client{ - Wallet: cfg.Wallet, - client: client, + wallet: cfg.Wallet, + policy: cfg.Policy, + Client: client, jobs: make(map[chainhash.Hash]*feePayment), + params: cfg.Params, } return v, nil } +// SetAccountInfo set the account purchase information +func (c *Client) SetAccountInfo(feeAcc, changeAcc int32) { + c.policy.FeeAcct = uint32(feeAcc) + c.policy.ChangeAcct = uint32(changeAcc) +} + func (c *Client) FeePercentage(ctx context.Context) (float64, error) { - var resp struct { - FeePercentage float64 `json:"feepercentage"` - } - err := c.get(ctx, "/api/v3/vspinfo", &resp) + resp, err := c.Client.VspInfo(ctx) if err != nil { return -1, err } return resp.FeePercentage, nil } -// ProcessUnprocessedTickets processes all tickets that don't currently have -// any association with a VSP. -func (c *Client) ProcessUnprocessedTickets(ctx context.Context, policy Policy) { +// ProcessUnprocessedTickets adds the provided tickets to the client. Noop if +// a given ticket is already added. +func (c *Client) ProcessUnprocessedTickets(ctx context.Context, tickets []*chainhash.Hash) { var wg sync.WaitGroup - c.Wallet.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error { - // Skip tickets which have a fee tx already associated with - // them; they are already processed by some vsp. - _, err := c.Wallet.VSPFeeHashForTicket(ctx, hash) - if err == nil { - return nil - } - confirmed, err := c.Wallet.IsVSPTicketConfirmed(ctx, hash) - if err != nil && !errors.Is(err, errors.NotExist) { - log.Error(err) - return nil - } - - if confirmed { - return nil - } + for _, hash := range tickets { c.mu.Lock() fp := c.jobs[*hash] c.mu.Unlock() if fp != nil { // Already processing this ticket with the VSP. - return nil + continue } // Start processing in the background. wg.Add(1) - go func() { + go func(ticketHash *chainhash.Hash) { defer wg.Done() - err := c.Process(ctx, hash, nil, policy) + err := c.Process(ctx, ticketHash, nil) if err != nil { log.Error(err) } - }() + }(hash) + } - return nil - }) wg.Wait() } -// ProcessTicket attempts to process a given ticket based on the hash provided. -func (c *Client) ProcessTicket(ctx context.Context, hash *chainhash.Hash, policy Policy) error { - err := c.Process(ctx, hash, nil, policy) - if err != nil { - return err - } - return nil -} - -// ProcessManagedTickets discovers tickets which were previously registered with -// a VSP and begins syncing them in the background. This is used to recover VSP -// tracking after seed restores, and is only performed on unspent and unexpired -// tickets. -func (c *Client) ProcessManagedTickets(ctx context.Context, policy Policy) error { - err := c.Wallet.ForUnspentUnexpiredTickets(ctx, func(hash *chainhash.Hash) error { - // We only want to process tickets that haven't been confirmed yet. - confirmed, err := c.Wallet.IsVSPTicketConfirmed(ctx, hash) - if err != nil && !errors.Is(err, errors.NotExist) { - log.Error(err) - return nil - } - if confirmed { - return nil - } +// ProcessManagedTickets adds the provided tickets to the client and resumes +// their fee payment process. Noop if a given ticket is already added, or if the +// ticket is not registered with the VSP. This is used to recover VSP tracking +// after seed restores. +func (c *Client) ProcessManagedTickets(ctx context.Context, tickets []*chainhash.Hash) error { + for _, hash := range tickets { c.mu.Lock() _, ok := c.jobs[*hash] c.mu.Unlock() if ok { // Already processing this ticket with the VSP. - return nil + continue } - // Make TicketStatus api call and only continue if ticket is + // Make ticketstatus api call and only continue if ticket is // found managed by this vsp. The rest is the same codepath as // for processing a new ticket. status, err := c.status(ctx, hash) @@ -165,7 +162,7 @@ func (c *Client) ProcessManagedTickets(ctx context.Context, policy Policy) error if errors.Is(err, errors.Locked) { return err } - return nil + continue } if status.FeeTxStatus == "confirmed" { @@ -173,7 +170,7 @@ func (c *Client) ProcessManagedTickets(ctx context.Context, policy Policy) error if err != nil { return err } - err = c.Wallet.UpdateVspTicketFeeToConfirmed(ctx, hash, feeHash, c.client.url, c.client.pub) + err = c.wallet.UpdateVspTicketFeeToConfirmed(ctx, hash, feeHash, c.Client.URL, c.Client.PubKey) if err != nil { return err } @@ -183,33 +180,31 @@ func (c *Client) ProcessManagedTickets(ctx context.Context, policy Policy) error if err != nil { return err } - err = c.Wallet.UpdateVspTicketFeeToPaid(ctx, hash, feeHash, c.client.url, c.client.pub) + err = c.wallet.UpdateVspTicketFeeToPaid(ctx, hash, feeHash, c.Client.URL, c.Client.PubKey) if err != nil { return err } - _ = c.feePayment(hash, policy, true) + _ = c.feePayment(ctx, hash, true) } else { // Fee hasn't been paid at the provided VSP, so this should do that if needed. - _ = c.feePayment(hash, policy, false) + _ = c.feePayment(ctx, hash, false) } - return nil - }) - return err + } + + return nil } // Process begins processing a VSP fee payment for a ticket. If feeTx contains // inputs, is used to pay the VSP fee. Otherwise, new inputs are selected and // locked to prevent double spending the fee. // -// feeTx must not be nil, but may point to an empty transaction, and is modified -// with the inputs and the fee and change outputs before returning without an -// error. The fee transaction is also recorded as unpublised in the wallet, and -// the fee hash is associated with the ticket. -func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx *wire.MsgTx, - policy Policy, -) error { - vspTicket, err := c.Wallet.VSPTicketInfo(ctx, ticketHash) +// feeTx may be nil or may point to an empty transaction. It is modified with +// the inputs and the fee and change outputs before returning without an error. +// The fee transaction is also recorded as unpublised in the wallet, and the fee +// hash is associated with the ticket. +func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx *wire.MsgTx) error { + vspTicket, err := c.wallet.VSPTicketInfo(ctx, ticketHash) if err != nil && !errors.Is(err, errors.NotExist) { return err } @@ -222,9 +217,9 @@ func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx case udb.VSPFeeProcessStarted, udb.VSPFeeProcessErrored: // If VSPTicket has been started or errored then attempt to create a new fee // transaction, submit it then confirm. - fp := c.feePayment(ticketHash, policy, false) + fp := c.feePayment(ctx, ticketHash, false) if fp == nil { - err := c.Wallet.UpdateVspTicketFeeToErrored(ctx, ticketHash, c.client.url, c.client.pub) + err := c.wallet.UpdateVspTicketFeeToErrored(ctx, ticketHash, c.Client.URL, c.Client.PubKey) if err != nil { return err } @@ -237,7 +232,7 @@ func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx fp.mu.Unlock() err := fp.receiveFeeAddress() if err != nil { - err := c.Wallet.UpdateVspTicketFeeToErrored(ctx, ticketHash, c.client.url, c.client.pub) + err := c.wallet.UpdateVspTicketFeeToErrored(ctx, ticketHash, c.Client.URL, c.Client.PubKey) if err != nil { return err } @@ -248,7 +243,7 @@ func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx } err = fp.makeFeeTx(feeTx) if err != nil { - err := c.Wallet.UpdateVspTicketFeeToErrored(ctx, ticketHash, c.client.url, c.client.pub) + err := c.wallet.UpdateVspTicketFeeToErrored(ctx, ticketHash, c.Client.URL, c.Client.PubKey) if err != nil { return err } @@ -257,11 +252,11 @@ func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx return fp.submitPayment() case udb.VSPFeeProcessPaid: // If a VSP ticket has been paid, but confirm payment. - if len(vspTicket.Host) > 0 && vspTicket.Host != c.client.url { + if len(vspTicket.Host) > 0 && vspTicket.Host != c.Client.URL { // Cannot confirm a paid ticket that is already with another VSP. return fmt.Errorf("ticket already paid or confirmed with another vsp") } - fp := c.feePayment(ticketHash, policy, true) + fp := c.feePayment(ctx, ticketHash, true) if fp == nil { // Don't update VSPStatus to Errored if it was already paid or // confirmed. @@ -281,7 +276,7 @@ func (c *Client) Process(ctx context.Context, ticketHash *chainhash.Hash, feeTx // the connected VSP. The status provides the current voting preferences so we // can just update from there if need be. func (c *Client) SetVoteChoice(ctx context.Context, hash *chainhash.Hash, - choices []wallet.AgendaChoice, tspendPolicy map[string]string, treasuryPolicy map[string]string, + choices map[string]string, tspendPolicy map[string]string, treasuryPolicy map[string]string, ) error { // Retrieve current voting preferences from VSP. status, err := c.status(ctx, hash) @@ -298,20 +293,18 @@ func (c *Client) SetVoteChoice(ctx context.Context, hash *chainhash.Hash, update := false // Check consensus vote choices. - for _, newChoice := range choices { - vspChoice, ok := status.VoteChoices[newChoice.AgendaID] + for newAgenda, newChoice := range choices { + vspChoice, ok := status.VoteChoices[newAgenda] if !ok { update = true break } - if vspChoice != newChoice.ChoiceID { + if vspChoice != newChoice { update = true break } } - // Apply the above changes to the two checks below. - // Check tspend policies. for newTSpend, newChoice := range tspendPolicy { vspChoice, ok := status.TSpendPolicy[newTSpend] @@ -356,7 +349,7 @@ type TicketInfo struct { TicketHash chainhash.Hash CommitmentAddr stdaddr.StakeAddress VotingAddr stdaddr.StakeAddress - State uint32 + State state Fee dcrutil.Amount FeeHash chainhash.Hash @@ -377,14 +370,14 @@ func (c *Client) TrackedTickets() []*TicketInfo { } c.mu.Unlock() - tickets := make([]*TicketInfo, 0, len(c.jobs)) + tickets := make([]*TicketInfo, 0, len(jobs)) for _, job := range jobs { job.mu.Lock() tickets = append(tickets, &TicketInfo{ TicketHash: job.ticketHash, CommitmentAddr: job.commitmentAddr, VotingAddr: job.votingAddr, - State: uint32(job.state), + State: job.state, Fee: job.fee, FeeHash: job.feeHash, }) diff --git a/libwallet/libwallet_suite_test.go b/libwallet/libwallet_suite_test.go index 021e8c6fc..b77098ae8 100644 --- a/libwallet/libwallet_suite_test.go +++ b/libwallet/libwallet_suite_test.go @@ -10,6 +10,6 @@ import ( func TestLibwallet(t *testing.T) { RegisterFailHandler(Fail) - rand.Seed(GinkgoRandomSeed()) + rand.New(rand.NewSource(GinkgoRandomSeed())) RunSpecs(t, "Libwallet Suite") } diff --git a/libwallet/log.go b/libwallet/log.go index b6effc459..8811f3b8a 100644 --- a/libwallet/log.go +++ b/libwallet/log.go @@ -8,7 +8,7 @@ package libwallet import ( "os" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/crypto-power/cryptopower/libwallet/internal/loader" "github.com/crypto-power/cryptopower/libwallet/internal/politeia" "github.com/crypto-power/cryptopower/libwallet/internal/vsp" diff --git a/libwallet/ltc.go b/libwallet/ltc.go index 3e1abe71c..1435d9858 100644 --- a/libwallet/ltc.go +++ b/libwallet/ltc.go @@ -3,7 +3,7 @@ package libwallet import ( "fmt" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/crypto-power/cryptopower/libwallet/assets/ltc" sharedW "github.com/crypto-power/cryptopower/libwallet/assets/wallet" "github.com/crypto-power/cryptopower/libwallet/utils" diff --git a/libwallet/txhelper/helper.go b/libwallet/txhelper/helper.go index 3243e1803..b780f078f 100644 --- a/libwallet/txhelper/helper.go +++ b/libwallet/txhelper/helper.go @@ -3,7 +3,7 @@ package txhelper import ( "math" - "decred.org/dcrwallet/v3/wallet" + "decred.org/dcrwallet/v4/wallet" "github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/wire" "github.com/decred/dcrdata/v8/txhelpers" diff --git a/libwallet/utils/errors.go b/libwallet/utils/errors.go index 73b2862bd..39ab4ef11 100644 --- a/libwallet/utils/errors.go +++ b/libwallet/utils/errors.go @@ -5,7 +5,7 @@ import ( "net" "strings" - "decred.org/dcrwallet/v3/errors" + "decred.org/dcrwallet/v4/errors" "github.com/asdine/storm" ) diff --git a/log.go b/log.go index ba6b36de1..9996a43c2 100644 --- a/log.go +++ b/log.go @@ -34,11 +34,11 @@ import ( "github.com/crypto-power/cryptopower/ui/page/transaction" "github.com/crypto-power/cryptopower/wallet" - "decred.org/dcrwallet/v3/p2p" - "decred.org/dcrwallet/v3/spv" - "decred.org/dcrwallet/v3/ticketbuyer" - dcrw "decred.org/dcrwallet/v3/wallet" - "decred.org/dcrwallet/v3/wallet/udb" + "decred.org/dcrwallet/v4/p2p" + "decred.org/dcrwallet/v4/spv" + "decred.org/dcrwallet/v4/ticketbuyer" + dcrw "decred.org/dcrwallet/v4/wallet" + "decred.org/dcrwallet/v4/wallet/udb" "github.com/btcsuite/btclog" btcC "github.com/btcsuite/btcwallet/chain" btcw "github.com/btcsuite/btcwallet/wallet" diff --git a/ui/page/seedbackup/verify_seed.go b/ui/page/seedbackup/verify_seed.go index c4baf3871..70fcf57a5 100644 --- a/ui/page/seedbackup/verify_seed.go +++ b/ui/page/seedbackup/verify_seed.go @@ -49,6 +49,7 @@ type VerifySeedPage struct { toggleSeedInput *cryptomaterial.Switch seedInputEditor cryptomaterial.Editor verifySeedButton cryptomaterial.Button + random *rand.Rand } func NewVerifySeedPage(l *load.Load, wallet sharedW.Asset, seed string, redirect Redirectfunc) *VerifySeedPage { @@ -68,7 +69,7 @@ func NewVerifySeedPage(l *load.Load, wallet sharedW.Asset, seed string, redirect Axis: layout.Vertical, }, } - + pg.random = rand.New(rand.NewSource(time.Now().UnixNano())) pg.actionButton.Font.Weight = font.Medium pg.backButton, _ = components.SubpageHeaderButtons(l) @@ -95,7 +96,6 @@ func (pg *VerifySeedPage) OnNavigatedTo() { listGroupSeed := make([]*layout.List, 0) multiSeedList := make([]shuffledSeedWords, 0) seedWords := strings.Split(pg.seed, " ") - rand.Seed(time.Now().UnixNano()) for _, word := range seedWords { listGroupSeed = append(listGroupSeed, &layout.List{Axis: layout.Horizontal}) index := seedPosition(word, allSeeds) @@ -126,14 +126,14 @@ func (pg *VerifySeedPage) getMultiSeed(realSeedIndex int, allSeeds []string) shu allSeeds = removeSeed(allSeeds, realSeedIndex) for i := 0; i < 3; i++ { - randomSeed := rand.Intn(len(allSeeds)) + randomSeed := pg.random.Intn(len(allSeeds)) shuffledSeed.words = append(shuffledSeed.words, allSeeds[randomSeed]) shuffledSeed.clickables = append(shuffledSeed.clickables, clickable()) allSeeds = removeSeed(allSeeds, randomSeed) } - rand.Shuffle(len(shuffledSeed.words), func(i, j int) { + pg.random.Shuffle(len(shuffledSeed.words), func(i, j int) { shuffledSeed.words[i], shuffledSeed.words[j] = shuffledSeed.words[j], shuffledSeed.words[i] }) diff --git a/ui/page/staking/stake_overview.go b/ui/page/staking/stake_overview.go index 6b6bb3988..cacc6ff55 100644 --- a/ui/page/staking/stake_overview.go +++ b/ui/page/staking/stake_overview.go @@ -340,9 +340,9 @@ func (pg *Page) HandleUserInteractions() { ticketTx.Hash, ticketInfo.FeeTxStatus.String(), ticketInfo.VSP) } - // Confirm that fee hasn't been paid, sender account exists, the wallet + // Confirm that fee processing errored, sender account exists, the wallet // is unlocked and no previous ticket processing instance is running. - if ticketInfo.FeeTxStatus != dcr.VSPFeeProcessPaid && len(ticketTx.Inputs) == 1 && + if ticketInfo.FeeTxStatus == dcr.VSPFeeProcessErrored && len(ticketTx.Inputs) == 1 && ticketInfo.Client != nil && atomic.CompareAndSwapUint32(&pg.processingTicket, 0, 1) { log.Infof("Attempting to process the unconfirmed VSP fee for tx: %v", ticketTx.Hash) @@ -353,8 +353,7 @@ func (pg *Page) HandleUserInteractions() { return } - account := ticketTx.Inputs[0].AccountNumber - err = ticketInfo.Client.ProcessTicket(pg.ctx, txHash, pg.dcrImpl.GetvspPolicy(account)) + err = ticketInfo.Client.Process(pg.ctx, txHash, nil) if err != nil { log.Errorf("processing the unconfirmed tx fee failed: %v", err) }