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  `