github.com/decred/politeia@v1.4.0/politeiawww/cmd/pictl/cmdcommentvote.go (about) 1 // Copyright (c) 2020-2020 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 "encoding/hex" 9 "fmt" 10 "strconv" 11 12 "github.com/decred/politeia/politeiad/api/v1/identity" 13 cmv1 "github.com/decred/politeia/politeiawww/api/comments/v1" 14 pclient "github.com/decred/politeia/politeiawww/client" 15 "github.com/decred/politeia/politeiawww/cmd/shared" 16 "github.com/decred/politeia/util" 17 ) 18 19 // cmdCommentVote is used to upvote/downvote a proposal comment using the 20 // logged in the user. 21 type cmdCommentVote struct { 22 Args struct { 23 Token string `positional-arg-name:"token"` 24 CommentID uint32 `positional-arg-name:"commentID"` 25 Vote string `positional-arg-name:"vote"` 26 } `positional-args:"true" required:"true"` 27 } 28 29 // Execute executes the cmdCommentVote command. 30 // 31 // This function satisfies the go-flags Commander interface. 32 func (c *cmdCommentVote) Execute(args []string) error { 33 // Check for user identity. A user identity is required to sign 34 // the comment vote. 35 if cfg.Identity == nil { 36 return shared.ErrUserIdentityNotFound 37 } 38 39 // Setup client 40 opts := pclient.Opts{ 41 HTTPSCert: cfg.HTTPSCert, 42 Cookies: cfg.Cookies, 43 HeaderCSRF: cfg.CSRF, 44 Verbose: cfg.Verbose, 45 RawJSON: cfg.RawJSON, 46 } 47 pc, err := pclient.New(cfg.Host, opts) 48 if err != nil { 49 return err 50 } 51 52 // Parse vote preference 53 votes := map[string]cmv1.VoteT{ 54 "upvote": cmv1.VoteUpvote, 55 "downvote": cmv1.VoteDownvote, 56 "1": cmv1.VoteUpvote, 57 "-1": cmv1.VoteDownvote, 58 } 59 vote, ok := votes[c.Args.Vote] 60 if !ok { 61 return fmt.Errorf("invalid vote option '%v' \n%v", 62 c.Args.Vote, commentVoteHelpMsg) 63 } 64 65 // Setup request 66 state := cmv1.RecordStateVetted 67 msg := strconv.FormatUint(uint64(state), 10) + c.Args.Token + 68 strconv.FormatUint(uint64(c.Args.CommentID), 10) + 69 strconv.FormatInt(int64(vote), 10) 70 sig := cfg.Identity.SignMessage([]byte(msg)) 71 v := cmv1.Vote{ 72 State: state, 73 Token: c.Args.Token, 74 CommentID: c.Args.CommentID, 75 Vote: vote, 76 Signature: hex.EncodeToString(sig[:]), 77 PublicKey: cfg.Identity.Public.String(), 78 } 79 80 // Send request 81 cvr, err := pc.CommentVote(v) 82 if err != nil { 83 return err 84 } 85 86 // Verify receipt 87 vr, err := client.Version() 88 if err != nil { 89 return err 90 } 91 serverID, err := identity.PublicIdentityFromString(vr.PubKey) 92 if err != nil { 93 return err 94 } 95 receiptb, err := util.ConvertSignature(cvr.Receipt) 96 if err != nil { 97 return err 98 } 99 if !serverID.VerifyMessage([]byte(v.Signature), receiptb) { 100 return fmt.Errorf("could not verify receipt") 101 } 102 103 // Print receipt 104 printf("Downvotes: %v\n", int64(cvr.Downvotes)*-1) 105 printf("Upvotes : %v\n", cvr.Upvotes) 106 printf("Timestamp: %v\n", dateAndTimeFromUnix(cvr.Timestamp)) 107 printf("Receipt : %v\n", cvr.Receipt) 108 109 return nil 110 } 111 112 // commentVoteHelpMsg is printed to stdout by the help command. 113 const commentVoteHelpMsg = `commentvote "token" "commentID" "vote" 114 115 Upvote or downvote a comment. 116 117 Requires the user to be logged in. Votes can only be cast on vetted records. 118 119 Arguments: 120 1. token (string, required) Proposal censorship token 121 2. commentID (string, required) Comment ID 122 3. vote (string, required) Upvote or downvote 123 124 You can specify either the numeric vote option (1 or -1) or the human readable 125 vote option. 126 upvote (1) 127 downvote (-1) 128 129 Example usage 130 $ commentvote d594fbadef0f9378 3 downvote 131 $ commentvote d594fbadef0f9378 3 -1 132 `