github.com/decred/politeia@v1.4.0/politeiawww/cmd/pictl/cmdvotetest.go (about) 1 // Copyright (c) 2020-2021 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "fmt" 9 "math/rand" 10 "strconv" 11 "sync" 12 "time" 13 14 tkv1 "github.com/decred/politeia/politeiawww/api/ticketvote/v1" 15 ) 16 17 // cmdVoteTest casts all eligible tickets in the user's wallet on all ongoing 18 // votes. 19 type cmdVoteTest struct { 20 Password string `long:"password" optional:"true"` 21 } 22 23 // Execute executes the cmdVoteTest command. 24 // 25 // This function satisfies the go-flags Commander interface. 26 func (c *cmdVoteTest) Execute(args []string) error { 27 // Prompt the user for their password if they haven't already 28 // provided it. 29 password := c.Password 30 if password == "" { 31 pass, err := promptWalletPassword() 32 if err != nil { 33 return err 34 } 35 password = string(pass) 36 } 37 38 // We don't want the output of individual commands printed. 39 cfg.Verbose = false 40 cfg.RawJSON = false 41 cfg.Silent = true 42 43 // Get all ongoing votes 44 votes := make([]string, 0, 256) 45 var page uint32 = 1 46 for { 47 tokens, err := voteInvForStatus(tkv1.VoteStatusStarted, page) 48 if err != nil { 49 return err 50 } 51 if len(tokens) == 0 { 52 // We've reached the end of the inventory 53 break 54 } 55 votes = append(votes, tokens...) 56 page++ 57 } 58 if len(votes) == 0 { 59 return fmt.Errorf("no ongoing votes") 60 } 61 62 // Setup vote options 63 voteOptions := []string{ 64 tkv1.VoteOptionIDApprove, 65 tkv1.VoteOptionIDReject, 66 } 67 68 // Cast ballots concurrently 69 var wg sync.WaitGroup 70 for _, v := range votes { 71 // Select vote option randomly 72 r := rand.Intn(len(voteOptions)) 73 voteOption := voteOptions[r] 74 75 wg.Add(1) 76 go func(wg *sync.WaitGroup, token, voteOption, password string) { 77 defer wg.Done() 78 79 // Turn printing back on for this part 80 cfg.Silent = false 81 82 // Cast ballot 83 fmt.Printf("Casting ballot for %v %v\n", token, voteOption) 84 start := time.Now() 85 err := castBallot(token, voteOption, password) 86 if err != nil { 87 fmt.Printf("castBallot %v: %v\n", token, err) 88 } 89 end := time.Now() 90 elapsed := end.Sub(start) 91 92 fmt.Printf("%v elapsed time %v\n", token, elapsed) 93 94 }(&wg, v, voteOption, password) 95 } 96 97 wg.Wait() 98 99 return nil 100 } 101 102 // voteInvForStatus returns a page of tokens for a vote status. 103 func voteInvForStatus(s tkv1.VoteStatusT, page uint32) ([]string, error) { 104 // Setup command 105 c := cmdVoteInv{} 106 c.Args.Status = strconv.Itoa(int(s)) 107 c.Args.Page = page 108 109 // Get inventory 110 inv, err := voteInv(&c) 111 if err != nil { 112 return nil, fmt.Errorf("cmdVoteInv: %v", err) 113 } 114 115 // Unpack reply 116 sm := tkv1.VoteStatuses[s] 117 return inv[sm], nil 118 } 119 120 func castBallot(token, voteID, password string) error { 121 c := cmdCastBallot{ 122 Password: password, 123 } 124 c.Args.Token = token 125 c.Args.VoteID = voteID 126 return c.Execute(nil) 127 } 128 129 // voteTestHelpMsg is the printed to stdout by the help command. 130 const voteTestHelpMsg = `votetest [flags] 131 132 Cast dcr ticket votes on all ongoing proposal votes. This command will randomly 133 select a vote option and cast all eligible tickets for that option. 134 135 dcrwallet must be running on localhost and listening on the default dcrwallet 136 port. 137 138 Flags: 139 --password (string, optional) dcrwallet password. The user will be prompted 140 for their password if one is not provided using 141 this flag. 142 `