From bb416e8bc9d02b0af165a489386618baeacf0884 Mon Sep 17 00:00:00 2001 From: Jamie Holdstock Date: Thu, 21 May 2020 07:59:51 +0100 Subject: [PATCH] Set vote choices on voting wallets (#43) --- README.md | 2 +- database/database_test.go | 29 +++++++----- database/ticket.go | 28 +++++------ webapi/getfeeaddress.go | 7 ++- webapi/helpers.go | 64 +++++++++++++------------ webapi/helpers_test.go | 52 +++++++++++++------- webapi/payfee.go | 21 +++++++-- webapi/setvotebits.go | 80 ------------------------------- webapi/setvotechoices.go | 99 +++++++++++++++++++++++++++++++++++++++ webapi/ticketstatus.go | 12 ++--- webapi/types.go | 36 +++++++------- webapi/webapi.go | 2 +- 12 files changed, 241 insertions(+), 191 deletions(-) delete mode 100644 webapi/setvotebits.go create mode 100644 webapi/setvotechoices.go diff --git a/README.md b/README.md index 1b7d457..1f9b93a 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ - Request fee address (`POST /feeaddress`) - Pay fee (`POST /payFee`) - Ticket status (`GET /ticketstatus`) - - Set voting preferences (`POST /setvotebits`) + - Set voting preferences (`POST /setvotechoices`) - A minimal, static, web front-end providing pool stats and basic connection instructions. - Fees have an expiry period. If the fee is not paid within this period, the client must request a new fee. This enables the VSP to alter its fee rate. diff --git a/database/database_test.go b/database/database_test.go index 04b0620..9f8d505 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -3,6 +3,7 @@ package database import ( "context" "os" + "reflect" "sync" "testing" ) @@ -20,7 +21,7 @@ func exampleTicket() Ticket { FeeAddress: "FeeAddress", SDiff: 1, BlockHeight: 2, - VoteBits: 3, + VoteChoices: map[string]string{"AgendaID": "Choice"}, VotingKey: "VotingKey", VSPFee: 0.1, Expiration: 4, @@ -38,7 +39,7 @@ func TestDatabase(t *testing.T) { "testGetTicketByHash": testGetTicketByHash, "testInsertFeeAddressVotingKey": testInsertFeeAddressVotingKey, "testUpdateExpireAndFee": testUpdateExpireAndFee, - "testUpdateVoteBits": testUpdateVoteBits, + "testUpdateVoteChoices": testUpdateVoteChoices, } for testName, test := range tests { @@ -105,7 +106,7 @@ func testGetTicketByHash(t *testing.T) { retrieved.FeeAddress != ticket.FeeAddress || retrieved.SDiff != ticket.SDiff || retrieved.BlockHeight != ticket.BlockHeight || - retrieved.VoteBits != ticket.VoteBits || + !reflect.DeepEqual(retrieved.VoteChoices, ticket.VoteChoices) || retrieved.VotingKey != ticket.VotingKey || retrieved.VSPFee != ticket.VSPFee || retrieved.Expiration != ticket.Expiration { @@ -129,10 +130,11 @@ func testInsertFeeAddressVotingKey(t *testing.T) { // Update values. newVotingKey := ticket.VotingKey + "2" - newVoteBits := ticket.VoteBits + 2 - err = db.InsertFeeAddressVotingKey(ticket.CommitmentAddress, newVotingKey, newVoteBits) + newVoteChoices := ticket.VoteChoices + newVoteChoices["AgendaID"] = "Different choice" + err = db.InsertFeeAddressVotingKey(ticket.CommitmentAddress, newVotingKey, newVoteChoices) if err != nil { - t.Fatalf("error updating votingkey and votebits: %v", err) + t.Fatalf("error updating votingkey and votechoices: %v", err) } // Retrieve ticket from database. @@ -142,7 +144,7 @@ func testInsertFeeAddressVotingKey(t *testing.T) { } // Check ticket fields match expected. - if newVoteBits != retrieved.VoteBits || + if !reflect.DeepEqual(newVoteChoices, retrieved.VoteChoices) || newVotingKey != retrieved.VotingKey { t.Fatal("retrieved ticket value didnt match expected") } @@ -176,7 +178,7 @@ func testUpdateExpireAndFee(t *testing.T) { } } -func testUpdateVoteBits(t *testing.T) { +func testUpdateVoteChoices(t *testing.T) { // Insert a ticket into the database. ticket := exampleTicket() err := db.InsertFeeAddress(ticket) @@ -184,11 +186,12 @@ func testUpdateVoteBits(t *testing.T) { t.Fatalf("error storing ticket in database: %v", err) } - // Update ticket with new votebits. - newVoteBits := ticket.VoteBits + 1 - err = db.UpdateVoteBits(ticket.Hash, newVoteBits) + // Update ticket with new votechoices. + newVoteChoices := ticket.VoteChoices + newVoteChoices["AgendaID"] = "Different choice" + err = db.UpdateVoteChoices(ticket.Hash, newVoteChoices) if err != nil { - t.Fatalf("error updating votebits: %v", err) + t.Fatalf("error updating votechoices: %v", err) } // Get updated ticket @@ -198,7 +201,7 @@ func testUpdateVoteBits(t *testing.T) { } // Check ticket fields match expected. - if retrieved.VoteBits != newVoteBits { + if !reflect.DeepEqual(newVoteChoices, retrieved.VoteChoices) { t.Fatal("retrieved ticket value didnt match expected") } } diff --git a/database/ticket.go b/database/ticket.go index d19a157..5040ca8 100644 --- a/database/ticket.go +++ b/database/ticket.go @@ -9,16 +9,16 @@ import ( ) type Ticket struct { - Hash string `json:"hash"` - CommitmentSignature string `json:"commitmentsignature"` - CommitmentAddress string `json:"commitmentaddress"` - FeeAddress string `json:"feeaddress"` - SDiff float64 `json:"sdiff"` - BlockHeight int64 `json:"blockheight"` - VoteBits uint16 `json:"votebits"` - VotingKey string `json:"votingkey"` - VSPFee float64 `json:"vspfee"` - Expiration int64 `json:"expiration"` + Hash string `json:"hash"` + CommitmentSignature string `json:"commitmentsignature"` + CommitmentAddress string `json:"commitmentaddress"` + FeeAddress string `json:"feeaddress"` + SDiff float64 `json:"sdiff"` + BlockHeight int64 `json:"blockheight"` + VoteChoices map[string]string `json:"votechoices"` + VotingKey string `json:"votingkey"` + VSPFee float64 `json:"vspfee"` + Expiration int64 `json:"expiration"` } var ( @@ -43,7 +43,7 @@ func (vdb *VspDatabase) InsertFeeAddress(ticket Ticket) error { }) } -func (vdb *VspDatabase) InsertFeeAddressVotingKey(address, votingKey string, voteBits uint16) error { +func (vdb *VspDatabase) InsertFeeAddressVotingKey(address, votingKey string, voteChoices map[string]string) error { return vdb.db.Update(func(tx *bolt.Tx) error { ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK) c := ticketBkt.Cursor() @@ -57,7 +57,7 @@ func (vdb *VspDatabase) InsertFeeAddressVotingKey(address, votingKey string, vot if ticket.CommitmentAddress == address { ticket.VotingKey = votingKey - ticket.VoteBits = voteBits + ticket.VoteChoices = voteChoices ticketBytes, err := json.Marshal(ticket) if err != nil { return err @@ -94,7 +94,7 @@ func (vdb *VspDatabase) GetTicketByHash(hash string) (Ticket, error) { return ticket, err } -func (vdb *VspDatabase) UpdateVoteBits(hash string, voteBits uint16) error { +func (vdb *VspDatabase) UpdateVoteChoices(hash string, voteChoices map[string]string) error { return vdb.db.Update(func(tx *bolt.Tx) error { ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK) key := []byte(hash) @@ -109,7 +109,7 @@ func (vdb *VspDatabase) UpdateVoteBits(hash string, voteBits uint16) error { if err != nil { return fmt.Errorf("could not unmarshal ticket: %v", err) } - ticket.VoteBits = voteBits + ticket.VoteChoices = voteChoices ticketBytes, err = json.Marshal(ticket) if err != nil { diff --git a/webapi/getfeeaddress.go b/webapi/getfeeaddress.go index 3310f5d..0201a04 100644 --- a/webapi/getfeeaddress.go +++ b/webapi/getfeeaddress.go @@ -38,7 +38,7 @@ func feeAddress(c *gin.Context) { // signature - sanity check signature is in base64 encoding signature := feeAddressRequest.Signature if _, err = base64.StdEncoding.DecodeString(signature); err != nil { - log.Warnf("Invalid signature from %s", c.ClientIP()) + log.Warnf("Invalid signature from %s: %v", c.ClientIP(), err) sendErrorResponse("invalid signature", http.StatusBadRequest, c) return } @@ -145,7 +145,7 @@ func feeAddress(c *gin.Context) { message := fmt.Sprintf("vsp v3 getfeeaddress %s", msgTx.TxHash()) err = dcrutil.VerifyMessage(addr.Address(), signature, message, cfg.NetParams) if err != nil { - log.Warnf("Invalid signature from %s", c.ClientIP()) + log.Warnf("Invalid signature from %s: %v", c.ClientIP(), err) sendErrorResponse("invalid signature", http.StatusBadRequest, c) return } @@ -179,10 +179,9 @@ func feeAddress(c *gin.Context) { FeeAddress: newAddress, SDiff: blockHeader.SBits, BlockHeight: int64(blockHeader.Height), - VoteBits: dcrutil.BlockValid, VSPFee: cfg.VSPFee, Expiration: expire, - // VotingKey: set during payfee + // VotingKey and VoteChoices: set during payfee } err = db.InsertFeeAddress(dbTicket) diff --git a/webapi/helpers.go b/webapi/helpers.go index 0a540da..c29da2c 100644 --- a/webapi/helpers.go +++ b/webapi/helpers.go @@ -1,41 +1,43 @@ package webapi import ( + "fmt" + "github.com/decred/dcrd/chaincfg/v3" - "github.com/decred/dcrd/dcrutil/v3" ) -// isValidVoteBits checks if voteBits are valid for the most recent agendas. -func isValidVoteBits(params *chaincfg.Params, voteBits uint16) bool { - - if !dcrutil.IsFlagSet16(voteBits, dcrutil.BlockValid) { - return false - } - voteBits &= ^uint16(dcrutil.BlockValid) - - // Get the most recent vote version. - var voteVersion uint32 +func currentVoteVersion(params *chaincfg.Params) uint32 { + var latestVersion uint32 for version := range params.Deployments { - if voteVersion < version { - voteVersion = version + if latestVersion < version { + latestVersion = version } } - - var availVoteBits uint16 - for _, vote := range params.Deployments[voteVersion] { - availVoteBits |= vote.Vote.Mask - - isValid := false - maskedBits := voteBits & vote.Vote.Mask - for _, c := range vote.Vote.Choices { - if c.Bits == maskedBits { - isValid = true - break - } - } - if !isValid { - return false - } - } - return true + return latestVersion +} + +// isValidVoteChoices returns an error if provided vote choices are not valid for +// the most recent agendas. +func isValidVoteChoices(params *chaincfg.Params, voteVersion uint32, voteChoices map[string]string) error { + +agendaLoop: + for agenda, choice := range voteChoices { + // Does the agenda exist? + for _, v := range params.Deployments[voteVersion] { + if v.Vote.Id == agenda { + // Agenda exists - does the vote choice exist? + for _, c := range v.Vote.Choices { + if c.Id == choice { + // Valid agenda and choice combo! Check the next one... + continue agendaLoop + } + } + return fmt.Errorf("choice %q not found for agenda %q", choice, agenda) + } + + } + return fmt.Errorf("agenda %q not found for vote version %d", agenda, voteVersion) + } + + return nil } diff --git a/webapi/helpers_test.go b/webapi/helpers_test.go index 9d06036..933392a 100644 --- a/webapi/helpers_test.go +++ b/webapi/helpers_test.go @@ -4,31 +4,47 @@ import ( "testing" "github.com/decred/dcrd/chaincfg/v3" - "github.com/decred/dcrd/dcrutil/v3" ) -func TestVoteBits(t *testing.T) { +func TestIsValidVoteChoices(t *testing.T) { + + // Mainnet vote version 4 contains 2 agendas - sdiffalgorithm and lnsupport. + // Both agendas have vote choices yes/no/abstain. + voteVersion := uint32(4) + params := chaincfg.MainNetParams() + var tests = []struct { - voteBits uint16 - isValid bool + voteChoices map[string]string + valid bool }{ - {0, false}, - {dcrutil.BlockValid, true}, - {dcrutil.BlockValid | 0x0002, true}, - {dcrutil.BlockValid | 0x0003, true}, - {dcrutil.BlockValid | 0x0004, true}, - {dcrutil.BlockValid | 0x0005, true}, - {dcrutil.BlockValid | 0x0006, false}, - {dcrutil.BlockValid | 0x0007, false}, - {dcrutil.BlockValid | 0x0008, true}, + // Empty vote choices are allowed. + {map[string]string{}, true}, + + // Valid agenda, valid vote choice. + {map[string]string{"lnsupport": "yes"}, true}, + {map[string]string{"sdiffalgorithm": "no", "lnsupport": "yes"}, true}, + + // Invalid agenda. + {map[string]string{"": "yes"}, false}, + {map[string]string{"Fake agenda": "yes"}, false}, + + // Valid agenda, invalid vote choice. + {map[string]string{"lnsupport": "1234"}, false}, + {map[string]string{"sdiffalgorithm": ""}, false}, + + // One valid choice, one invalid choice. + {map[string]string{"sdiffalgorithm": "no", "lnsupport": "1234"}, false}, + {map[string]string{"sdiffalgorithm": "1234", "lnsupport": "no"}, false}, + + // One valid agenda, one invalid agenda. + {map[string]string{"fake": "abstain", "lnsupport": "no"}, false}, + {map[string]string{"sdiffalgorithm": "abstain", "": "no"}, false}, } - params := chaincfg.MainNetParams() for _, test := range tests { - isValid := isValidVoteBits(params, test.voteBits) - if isValid != test.isValid { - t.Fatalf("isValidVoteBits failed for votebits '%d': want %v, got %v", - test.voteBits, test.isValid, isValid) + err := isValidVoteChoices(params, voteVersion, test.voteChoices) + if (err == nil) != test.valid { + t.Fatalf("isValidVoteChoices failed for votechoices '%v'.", test.voteChoices) } } } diff --git a/webapi/payfee.go b/webapi/payfee.go index 5a98fda..5ac8479 100644 --- a/webapi/payfee.go +++ b/webapi/payfee.go @@ -33,10 +33,11 @@ func payFee(c *gin.Context) { return } - voteBits := payFeeRequest.VoteBits - if !isValidVoteBits(cfg.NetParams, voteBits) { - log.Warnf("Invalid votebits from %s", c.ClientIP()) - sendErrorResponse("invalid votebits", http.StatusBadRequest, c) + voteChoices := payFeeRequest.VoteChoices + err = isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices) + if err != nil { + log.Warnf("Invalid votechoices from %s: %v", c.ClientIP(), err) + sendErrorResponse(err.Error(), http.StatusBadRequest, c) return } @@ -161,6 +162,16 @@ findAddress: return } + // Update vote choices on voting wallets. + for agenda, choice := range voteChoices { + err = walletClient.Call(ctx, "setvotechoice", nil, agenda, choice, ticket.Hash) + if err != nil { + log.Errorf("setvotechoice failed: %v", err) + sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c) + return + } + } + feeTxBuf := new(bytes.Buffer) feeTxBuf.Grow(feeTx.SerializeSize()) err = feeTx.Serialize(feeTxBuf) @@ -178,7 +189,7 @@ findAddress: return } - err = db.InsertFeeAddressVotingKey(voteAddr.Address(), votingWIF.String(), voteBits) + err = db.InsertFeeAddressVotingKey(voteAddr.Address(), votingWIF.String(), voteChoices) if err != nil { log.Errorf("InsertFeeAddressVotingKey failed: %v", err) sendErrorResponse("database error", http.StatusInternalServerError, c) diff --git a/webapi/setvotebits.go b/webapi/setvotebits.go deleted file mode 100644 index d037e96..0000000 --- a/webapi/setvotebits.go +++ /dev/null @@ -1,80 +0,0 @@ -package webapi - -import ( - "encoding/base64" - "fmt" - "net/http" - "time" - - "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/dcrutil/v3" - "github.com/gin-gonic/gin" -) - -// setVoteBits is the handler for "POST /setvotebits" -func setVoteBits(c *gin.Context) { - var setVoteBitsRequest SetVoteBitsRequest - if err := c.ShouldBindJSON(&setVoteBitsRequest); err != nil { - log.Warnf("Bad setvotebits request from %s: %v", c.ClientIP(), err) - sendErrorResponse(err.Error(), http.StatusBadRequest, c) - return - } - - // ticketHash - ticketHashStr := setVoteBitsRequest.TicketHash - txHash, err := chainhash.NewHashFromStr(ticketHashStr) - if err != nil { - log.Warnf("Invalid ticket hash from %s", c.ClientIP()) - sendErrorResponse("invalid ticket hash", http.StatusBadRequest, c) - return - } - - // signature - sanity check signature is in base64 encoding - signature := setVoteBitsRequest.Signature - if _, err = base64.StdEncoding.DecodeString(signature); err != nil { - log.Warnf("Invalid signature from %s", c.ClientIP()) - sendErrorResponse("invalid signature", http.StatusBadRequest, c) - return - } - - // votebits - voteBits := setVoteBitsRequest.VoteBits - if !isValidVoteBits(cfg.NetParams, voteBits) { - log.Warnf("Invalid votebits from %s", c.ClientIP()) - sendErrorResponse("invalid votebits", http.StatusBadRequest, c) - return - } - - ticket, err := db.GetTicketByHash(txHash.String()) - if err != nil { - log.Warnf("Invalid ticket from %s", c.ClientIP()) - sendErrorResponse("invalid ticket", http.StatusBadRequest, c) - return - } - - // verify message - message := fmt.Sprintf("vsp v3 setvotebits %d %s %d", setVoteBitsRequest.Timestamp, txHash, voteBits) - err = dcrutil.VerifyMessage(ticket.CommitmentAddress, signature, message, cfg.NetParams) - if err != nil { - log.Warnf("Failed to verify message from %s", c.ClientIP()) - sendErrorResponse("message did not pass verification", http.StatusBadRequest, c) - return - } - - err = db.UpdateVoteBits(txHash.String(), voteBits) - if err != nil { - log.Errorf("UpdateVoteBits error: %v", err) - sendErrorResponse("database error", http.StatusInternalServerError, c) - return - } - - // TODO: DB - error if given timestamp is older than any previous requests - - // TODO: DB - store setvotebits receipt in log - - sendJSONResponse(setVoteBitsResponse{ - Timestamp: time.Now().Unix(), - Request: setVoteBitsRequest, - VoteBits: voteBits, - }, c) -} diff --git a/webapi/setvotechoices.go b/webapi/setvotechoices.go new file mode 100644 index 0000000..ba94a89 --- /dev/null +++ b/webapi/setvotechoices.go @@ -0,0 +1,99 @@ +package webapi + +import ( + "encoding/base64" + "fmt" + "net/http" + "time" + + "github.com/decred/dcrd/chaincfg/chainhash" + "github.com/decred/dcrd/dcrutil/v3" + "github.com/gin-gonic/gin" +) + +// setVoteChoices is the handler for "POST /setvotechoices" +func setVoteChoices(c *gin.Context) { + var setVoteChoicesRequest SetVoteChoicesRequest + if err := c.ShouldBindJSON(&setVoteChoicesRequest); err != nil { + log.Warnf("Bad setvotechoices request from %s: %v", c.ClientIP(), err) + sendErrorResponse(err.Error(), http.StatusBadRequest, c) + return + } + + // ticketHash + ticketHashStr := setVoteChoicesRequest.TicketHash + txHash, err := chainhash.NewHashFromStr(ticketHashStr) + if err != nil { + log.Warnf("Invalid ticket hash from %s", c.ClientIP()) + sendErrorResponse("invalid ticket hash", http.StatusBadRequest, c) + return + } + + // signature - sanity check signature is in base64 encoding + signature := setVoteChoicesRequest.Signature + if _, err = base64.StdEncoding.DecodeString(signature); err != nil { + log.Warnf("Invalid signature from %s: %v", c.ClientIP(), err) + sendErrorResponse("invalid signature", http.StatusBadRequest, c) + return + } + + voteChoices := setVoteChoicesRequest.VoteChoices + err = isValidVoteChoices(cfg.NetParams, currentVoteVersion(cfg.NetParams), voteChoices) + if err != nil { + log.Warnf("Invalid votechoices from %s: %v", c.ClientIP(), err) + sendErrorResponse(err.Error(), http.StatusBadRequest, c) + return + } + + ticket, err := db.GetTicketByHash(txHash.String()) + if err != nil { + log.Warnf("Invalid ticket from %s", c.ClientIP()) + sendErrorResponse("invalid ticket", http.StatusBadRequest, c) + return + } + + // verify message + message := fmt.Sprintf("vsp v3 setvotechoices %d %s %v", setVoteChoicesRequest.Timestamp, txHash, voteChoices) + err = dcrutil.VerifyMessage(ticket.CommitmentAddress, signature, message, cfg.NetParams) + if err != nil { + log.Warnf("Failed to verify message from %s: %v", c.ClientIP(), err) + sendErrorResponse("message did not pass verification", http.StatusBadRequest, c) + return + } + + walletClient, err := walletRPC() + if err != nil { + log.Errorf("Failed to dial dcrwallet RPC: %v", err) + sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c) + return + } + + ctx := c.Request.Context() + + // Update vote choices on voting wallets. + for agenda, choice := range voteChoices { + err = walletClient.Call(ctx, "setvotechoice", nil, agenda, choice, ticket.Hash) + if err != nil { + log.Errorf("setvotechoice failed: %v", err) + sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c) + return + } + } + + err = db.UpdateVoteChoices(txHash.String(), voteChoices) + if err != nil { + log.Errorf("UpdateVoteChoices error: %v", err) + sendErrorResponse("database error", http.StatusInternalServerError, c) + return + } + + // TODO: DB - error if given timestamp is older than any previous requests + + // TODO: DB - store setvotechoices receipt in log + + sendJSONResponse(setVoteChoicesResponse{ + Timestamp: time.Now().Unix(), + Request: setVoteChoicesRequest, + VoteChoices: voteChoices, + }, c) +} diff --git a/webapi/ticketstatus.go b/webapi/ticketstatus.go index 165b48f..d059d74 100644 --- a/webapi/ticketstatus.go +++ b/webapi/ticketstatus.go @@ -32,7 +32,7 @@ func ticketStatus(c *gin.Context) { // signature - sanity check signature is in base64 encoding signature := ticketStatusRequest.Signature if _, err = base64.StdEncoding.DecodeString(signature); err != nil { - log.Warnf("Invalid signature from %s", c.ClientIP()) + log.Warnf("Invalid signature from %s: %v", c.ClientIP(), err) sendErrorResponse("invalid signature", http.StatusBadRequest, c) return } @@ -48,15 +48,15 @@ func ticketStatus(c *gin.Context) { message := fmt.Sprintf("vsp v3 ticketstatus %d %s", ticketStatusRequest.Timestamp, ticketHashStr) err = dcrutil.VerifyMessage(ticket.CommitmentAddress, signature, message, cfg.NetParams) if err != nil { - log.Warnf("Invalid signature from %s", c.ClientIP()) + log.Warnf("Invalid signature from %s: %v", c.ClientIP(), err) sendErrorResponse("invalid signature", http.StatusBadRequest, c) return } sendJSONResponse(ticketStatusResponse{ - Timestamp: time.Now().Unix(), - Request: ticketStatusRequest, - Status: "active", // TODO - active, pending, expired (missed, revoked?) - VoteBits: ticket.VoteBits, + Timestamp: time.Now().Unix(), + Request: ticketStatusRequest, + Status: "active", // TODO - active, pending, expired (missed, revoked?) + VoteChoices: ticket.VoteChoices, }, c) } diff --git a/webapi/types.go b/webapi/types.go index 78ed8b6..5c1028e 100644 --- a/webapi/types.go +++ b/webapi/types.go @@ -25,11 +25,11 @@ type feeAddressResponse struct { } type PayFeeRequest struct { - Timestamp int64 `json:"timestamp" binding:"required"` - TicketHash string `json:"tickethash" binding:"required"` - FeeTx string `json:"feetx" binding:"required"` - VotingKey string `json:"votingkey" binding:"required"` - VoteBits uint16 `json:"votebits" binding:"required"` + Timestamp int64 `json:"timestamp" binding:"required"` + TicketHash string `json:"tickethash" binding:"required"` + FeeTx string `json:"feetx" binding:"required"` + VotingKey string `json:"votingkey" binding:"required"` + VoteChoices map[string]string `json:"votechoices" binding:"required"` } type payFeeResponse struct { @@ -38,17 +38,17 @@ type payFeeResponse struct { Request PayFeeRequest `json:"request" binding:"required"` } -type SetVoteBitsRequest struct { - Timestamp int64 `json:"timestamp" binding:"required"` - TicketHash string `json:"tickethash" binding:"required"` - Signature string `json:"commitmentsignature" binding:"required"` - VoteBits uint16 `json:"votebits" binding:"required"` +type SetVoteChoicesRequest struct { + Timestamp int64 `json:"timestamp" binding:"required"` + TicketHash string `json:"tickethash" binding:"required"` + Signature string `json:"commitmentsignature" binding:"required"` + VoteChoices map[string]string `json:"votechoices" binding:"required"` } -type setVoteBitsResponse struct { - Timestamp int64 `json:"timestamp" binding:"required"` - Request SetVoteBitsRequest `json:"request" binding:"required"` - VoteBits uint16 `json:"votebits" binding:"required"` +type setVoteChoicesResponse struct { + Timestamp int64 `json:"timestamp" binding:"required"` + Request SetVoteChoicesRequest `json:"request" binding:"required"` + VoteChoices map[string]string `json:"votechoices" binding:"required"` } type TicketStatusRequest struct { @@ -58,8 +58,8 @@ type TicketStatusRequest struct { } type ticketStatusResponse struct { - Timestamp int64 `json:"timestamp" binding:"required"` - Request TicketStatusRequest `json:"request" binding:"required"` - Status string `json:"status" binding:"required"` - VoteBits uint16 `json:"votebits" binding:"required"` + Timestamp int64 `json:"timestamp" binding:"required"` + Request TicketStatusRequest `json:"request" binding:"required"` + Status string `json:"status" binding:"required"` + VoteChoices map[string]string `json:"votechoices" binding:"required"` } diff --git a/webapi/webapi.go b/webapi/webapi.go index 1c17674..bc7313d 100644 --- a/webapi/webapi.go +++ b/webapi/webapi.go @@ -116,7 +116,7 @@ func router(debugMode bool) *gin.Engine { api.POST("/feeaddress", feeAddress) api.GET("/pubkey", pubKey) api.POST("/payfee", payFee) - api.POST("/setvotebits", setVoteBits) + api.POST("/setvotechoices", setVoteChoices) api.GET("/ticketstatus", ticketStatus) }