github.com/decred/politeia@v1.4.0/politeiawww/client/ticketvote.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 client 6 7 import ( 8 "encoding/base64" 9 "encoding/hex" 10 "encoding/json" 11 "fmt" 12 "net/http" 13 "strconv" 14 15 "github.com/decred/dcrd/chaincfg/v3" 16 backend "github.com/decred/politeia/politeiad/backendv2" 17 tkv1 "github.com/decred/politeia/politeiawww/api/ticketvote/v1" 18 "github.com/decred/politeia/util" 19 ) 20 21 // TicketVotePolicy sends a ticketvote v1 Policy request to politeiawww. 22 func (c *Client) TicketVotePolicy() (*tkv1.PolicyReply, error) { 23 resBody, err := c.makeReq(http.MethodPost, 24 tkv1.APIRoute, tkv1.RoutePolicy, nil) 25 if err != nil { 26 return nil, err 27 } 28 29 var pr tkv1.PolicyReply 30 err = json.Unmarshal(resBody, &pr) 31 if err != nil { 32 return nil, err 33 } 34 35 return &pr, nil 36 } 37 38 // TicketVoteAuthorize sends a ticketvote v1 Authorize request to politeiawww. 39 func (c *Client) TicketVoteAuthorize(a tkv1.Authorize) (*tkv1.AuthorizeReply, error) { 40 resBody, err := c.makeReq(http.MethodPost, 41 tkv1.APIRoute, tkv1.RouteAuthorize, a) 42 if err != nil { 43 return nil, err 44 } 45 46 var ar tkv1.AuthorizeReply 47 err = json.Unmarshal(resBody, &ar) 48 if err != nil { 49 return nil, err 50 } 51 52 return &ar, nil 53 } 54 55 // TicketVoteStart sends a ticketvote v1 Start request to politeiawww. 56 func (c *Client) TicketVoteStart(s tkv1.Start) (*tkv1.StartReply, error) { 57 resBody, err := c.makeReq(http.MethodPost, 58 tkv1.APIRoute, tkv1.RouteStart, s) 59 if err != nil { 60 return nil, err 61 } 62 63 var sr tkv1.StartReply 64 err = json.Unmarshal(resBody, &sr) 65 if err != nil { 66 return nil, err 67 } 68 69 return &sr, nil 70 } 71 72 // TicketVoteCastBallot sends a ticketvote v1 CastBallot request to 73 // politeiawww. 74 func (c *Client) TicketVoteCastBallot(cb tkv1.CastBallot) (*tkv1.CastBallotReply, error) { 75 resBody, err := c.makeReq(http.MethodPost, 76 tkv1.APIRoute, tkv1.RouteCastBallot, cb) 77 if err != nil { 78 return nil, err 79 } 80 81 var cbr tkv1.CastBallotReply 82 err = json.Unmarshal(resBody, &cbr) 83 if err != nil { 84 return nil, err 85 } 86 87 return &cbr, nil 88 } 89 90 // TicketVoteDetails sends a ticketvote v1 Details request to politeiawww. 91 func (c *Client) TicketVoteDetails(d tkv1.Details) (*tkv1.DetailsReply, error) { 92 resBody, err := c.makeReq(http.MethodPost, 93 tkv1.APIRoute, tkv1.RouteDetails, d) 94 if err != nil { 95 return nil, err 96 } 97 98 var dr tkv1.DetailsReply 99 err = json.Unmarshal(resBody, &dr) 100 if err != nil { 101 return nil, err 102 } 103 104 return &dr, nil 105 } 106 107 // TicketVoteResults sends a ticketvote v1 Results request to politeiawww. 108 func (c *Client) TicketVoteResults(r tkv1.Results) (*tkv1.ResultsReply, error) { 109 resBody, err := c.makeReq(http.MethodPost, 110 tkv1.APIRoute, tkv1.RouteResults, r) 111 if err != nil { 112 return nil, err 113 } 114 115 var rr tkv1.ResultsReply 116 err = json.Unmarshal(resBody, &rr) 117 if err != nil { 118 return nil, err 119 } 120 121 return &rr, nil 122 } 123 124 // TicketVoteSummaries sends a ticketvote v1 Summaries request to politeiawww. 125 func (c *Client) TicketVoteSummaries(s tkv1.Summaries) (*tkv1.SummariesReply, error) { 126 resBody, err := c.makeReq(http.MethodPost, 127 tkv1.APIRoute, tkv1.RouteSummaries, s) 128 if err != nil { 129 return nil, err 130 } 131 132 var sr tkv1.SummariesReply 133 err = json.Unmarshal(resBody, &sr) 134 if err != nil { 135 return nil, err 136 } 137 138 return &sr, nil 139 } 140 141 // TicketVoteSubmissions sends a ticketvote v1 Submissions request to 142 // politeiawww. 143 func (c *Client) TicketVoteSubmissions(s tkv1.Submissions) (*tkv1.SubmissionsReply, error) { 144 resBody, err := c.makeReq(http.MethodPost, 145 tkv1.APIRoute, tkv1.RouteSubmissions, s) 146 if err != nil { 147 return nil, err 148 } 149 150 var sr tkv1.SubmissionsReply 151 err = json.Unmarshal(resBody, &sr) 152 if err != nil { 153 return nil, err 154 } 155 156 return &sr, nil 157 } 158 159 // TicketVoteInventory sends a ticketvote v1 Inventory request to politeiawww. 160 func (c *Client) TicketVoteInventory(i tkv1.Inventory) (*tkv1.InventoryReply, error) { 161 resBody, err := c.makeReq(http.MethodPost, 162 tkv1.APIRoute, tkv1.RouteInventory, i) 163 if err != nil { 164 return nil, err 165 } 166 167 var ir tkv1.InventoryReply 168 err = json.Unmarshal(resBody, &ir) 169 if err != nil { 170 return nil, err 171 } 172 173 return &ir, nil 174 } 175 176 // TicketVoteTimestamps sends a ticketvote v1 Timestamps request to 177 // politeiawww. 178 func (c *Client) TicketVoteTimestamps(t tkv1.Timestamps) (*tkv1.TimestampsReply, error) { 179 resBody, err := c.makeReq(http.MethodPost, 180 tkv1.APIRoute, tkv1.RouteTimestamps, t) 181 if err != nil { 182 return nil, err 183 } 184 185 var tr tkv1.TimestampsReply 186 err = json.Unmarshal(resBody, &tr) 187 if err != nil { 188 return nil, err 189 } 190 191 return &tr, nil 192 } 193 194 // TicketVoteTimestampVerify verifies that the provided ticketvote v1 Timestamp 195 // is valid. 196 func TicketVoteTimestampVerify(t tkv1.Timestamp) error { 197 return backend.VerifyTimestamp(convertVoteTimestamp(t)) 198 } 199 200 // TicketVoteTimestampsVerify verifies that all timestamps in the ticketvote 201 // v1 TimestampsReply are valid. 202 func TicketVoteTimestampsVerify(tr tkv1.TimestampsReply) error { 203 // Verify authorization timestamps 204 for k, v := range tr.Auths { 205 err := TicketVoteTimestampVerify(v) 206 if err != nil { 207 return fmt.Errorf("verify authorization %v timestamp: %v", k, err) 208 } 209 } 210 211 // Verify vote details timestamp 212 if tr.Details != nil { 213 err := TicketVoteTimestampVerify(*tr.Details) 214 if err != nil { 215 return fmt.Errorf("verify vote details timestamp: %v", err) 216 } 217 } 218 219 // Verify vote timestamps 220 for k, v := range tr.Votes { 221 err := TicketVoteTimestampVerify(v) 222 if err != nil { 223 return fmt.Errorf("verify vote %v timestamp: %v", k, err) 224 } 225 } 226 227 return nil 228 } 229 230 // AuthDetailsVerify verifies the action, signature, and receipt of the 231 // provided ticketvote v1 AuthDetails. 232 func AuthDetailsVerify(a tkv1.AuthDetails, serverPublicKey string) error { 233 // Verify action 234 switch tkv1.AuthActionT(a.Action) { 235 case tkv1.AuthActionAuthorize, tkv1.AuthActionRevoke: 236 // These are allowed; continue 237 default: 238 return fmt.Errorf("invalid auth action '%v'", a.Action) 239 } 240 241 // Verify signature 242 msg := a.Token + strconv.FormatUint(uint64(a.Version), 10) + a.Action 243 err := util.VerifySignature(a.Signature, a.PublicKey, msg) 244 if err != nil { 245 return fmt.Errorf("verify signature: %v", err) 246 } 247 248 // Verify receipt 249 err = util.VerifySignature(a.Receipt, serverPublicKey, a.Signature) 250 if err != nil { 251 return fmt.Errorf("verify receipt: %v", err) 252 } 253 254 return nil 255 } 256 257 // VoteDetailsVerify verifies the signature and receipt of the provided 258 // ticketvote v1 VoteDetails. 259 func VoteDetailsVerify(vd tkv1.VoteDetails, serverPublicKey string) error { 260 // Verify client signature 261 b, err := json.Marshal(vd.Params) 262 if err != nil { 263 return err 264 } 265 msg := hex.EncodeToString(util.Digest(b)) 266 err = util.VerifySignature(vd.Signature, vd.PublicKey, msg) 267 if err != nil { 268 return fmt.Errorf("could not verify signature: %v", err) 269 } 270 271 // Make sure we have valid vote bits. 272 switch { 273 case vd.Params.Token == "": 274 return fmt.Errorf("token not found") 275 case vd.Params.Mask == 0: 276 return fmt.Errorf("mask not found") 277 case len(vd.Params.Options) == 0: 278 return fmt.Errorf("vote options not found") 279 } 280 281 // Verify server receipt 282 msg = vd.Signature + vd.StartBlockHash 283 err = util.VerifySignature(vd.Receipt, serverPublicKey, msg) 284 if err != nil { 285 return fmt.Errorf("could not verify receipt: %v", err) 286 } 287 288 return nil 289 } 290 291 // CastVoteDetailsVerify verifies the receipt of the provided ticketvote v1 292 // CastVoteDetails. 293 func CastVoteDetailsVerify(cvd tkv1.CastVoteDetails, serverPublicKey string) error { 294 // The network must be ascertained in order to verify the 295 // signature. We can do this by looking at the P2PKH prefix. 296 var net *chaincfg.Params 297 switch cvd.Address[:2] { 298 case "Ds": 299 // Mainnet 300 net = chaincfg.MainNetParams() 301 case "Ts": 302 // Testnet 303 net = chaincfg.TestNet3Params() 304 case "Ss": 305 // Simnet 306 net = chaincfg.SimNetParams() 307 default: 308 return fmt.Errorf("unknown p2pkh address %v", cvd.Address) 309 } 310 311 // Verify signature. The signature must be converted from hex to 312 // base64. This is what the verify message function expects. 313 msg := cvd.Token + cvd.Ticket + cvd.VoteBit 314 b, err := hex.DecodeString(cvd.Signature) 315 if err != nil { 316 return fmt.Errorf("signature invalid hex") 317 } 318 sig := base64.StdEncoding.EncodeToString(b) 319 validated, err := util.VerifyMessage(cvd.Address, msg, sig, net) 320 if err != nil { 321 return err 322 } 323 if !validated { 324 return fmt.Errorf("invalid cast vote signature") 325 } 326 327 // Verify receipt 328 err = util.VerifySignature(cvd.Receipt, serverPublicKey, cvd.Signature) 329 if err != nil { 330 return fmt.Errorf("could not verify receipt: %v", err) 331 } 332 333 return nil 334 } 335 336 func convertVoteProof(p tkv1.Proof) backend.Proof { 337 return backend.Proof{ 338 Type: p.Type, 339 Digest: p.Digest, 340 MerkleRoot: p.MerkleRoot, 341 MerklePath: p.MerklePath, 342 ExtraData: p.ExtraData, 343 } 344 } 345 346 func convertVoteTimestamp(t tkv1.Timestamp) backend.Timestamp { 347 proofs := make([]backend.Proof, 0, len(t.Proofs)) 348 for _, v := range t.Proofs { 349 proofs = append(proofs, convertVoteProof(v)) 350 } 351 return backend.Timestamp{ 352 Data: t.Data, 353 Digest: t.Digest, 354 TxID: t.TxID, 355 MerkleRoot: t.MerkleRoot, 356 Proofs: proofs, 357 } 358 }