Cleaning up some TODOs (#50)
This commit is contained in:
parent
740b9c8e0f
commit
96608718a0
@ -24,7 +24,8 @@ func exampleTicket() Ticket {
|
|||||||
VoteChoices: map[string]string{"AgendaID": "Choice"},
|
VoteChoices: map[string]string{"AgendaID": "Choice"},
|
||||||
VotingKey: "VotingKey",
|
VotingKey: "VotingKey",
|
||||||
VSPFee: 0.1,
|
VSPFee: 0.1,
|
||||||
Expiration: 4,
|
FeeExpiration: 4,
|
||||||
|
FeeTxHash: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,7 +110,8 @@ func testGetTicketByHash(t *testing.T) {
|
|||||||
!reflect.DeepEqual(retrieved.VoteChoices, ticket.VoteChoices) ||
|
!reflect.DeepEqual(retrieved.VoteChoices, ticket.VoteChoices) ||
|
||||||
retrieved.VotingKey != ticket.VotingKey ||
|
retrieved.VotingKey != ticket.VotingKey ||
|
||||||
retrieved.VSPFee != ticket.VSPFee ||
|
retrieved.VSPFee != ticket.VSPFee ||
|
||||||
retrieved.Expiration != ticket.Expiration {
|
retrieved.FeeTxHash != ticket.FeeTxHash ||
|
||||||
|
retrieved.FeeExpiration != ticket.FeeExpiration {
|
||||||
t.Fatal("retrieved ticket value didnt match expected")
|
t.Fatal("retrieved ticket value didnt match expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,8 +133,9 @@ func testSetTicketVotingKey(t *testing.T) {
|
|||||||
// Update values.
|
// Update values.
|
||||||
newVotingKey := ticket.VotingKey + "2"
|
newVotingKey := ticket.VotingKey + "2"
|
||||||
newVoteChoices := ticket.VoteChoices
|
newVoteChoices := ticket.VoteChoices
|
||||||
|
feeTxHash := ticket.FeeTxHash + "3"
|
||||||
newVoteChoices["AgendaID"] = "Different choice"
|
newVoteChoices["AgendaID"] = "Different choice"
|
||||||
err = db.SetTicketVotingKey(ticket.Hash, newVotingKey, newVoteChoices)
|
err = db.SetTicketVotingKey(ticket.Hash, newVotingKey, newVoteChoices, feeTxHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error updating votingkey and votechoices: %v", err)
|
t.Fatalf("error updating votingkey and votechoices: %v", err)
|
||||||
}
|
}
|
||||||
@ -145,6 +148,7 @@ func testSetTicketVotingKey(t *testing.T) {
|
|||||||
|
|
||||||
// Check ticket fields match expected.
|
// Check ticket fields match expected.
|
||||||
if !reflect.DeepEqual(newVoteChoices, retrieved.VoteChoices) ||
|
if !reflect.DeepEqual(newVoteChoices, retrieved.VoteChoices) ||
|
||||||
|
feeTxHash != retrieved.FeeTxHash ||
|
||||||
newVotingKey != retrieved.VotingKey {
|
newVotingKey != retrieved.VotingKey {
|
||||||
t.Fatal("retrieved ticket value didnt match expected")
|
t.Fatal("retrieved ticket value didnt match expected")
|
||||||
}
|
}
|
||||||
@ -159,7 +163,7 @@ func testUpdateExpireAndFee(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Update ticket with new values.
|
// Update ticket with new values.
|
||||||
newExpiry := ticket.Expiration + 1
|
newExpiry := ticket.FeeExpiration + 1
|
||||||
newFee := ticket.VSPFee + 1
|
newFee := ticket.VSPFee + 1
|
||||||
err = db.UpdateExpireAndFee(ticket.Hash, newExpiry, newFee)
|
err = db.UpdateExpireAndFee(ticket.Hash, newExpiry, newFee)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -173,7 +177,7 @@ func testUpdateExpireAndFee(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check ticket fields match expected.
|
// Check ticket fields match expected.
|
||||||
if retrieved.VSPFee != newFee || retrieved.Expiration != newExpiry {
|
if retrieved.VSPFee != newFee || retrieved.FeeExpiration != newExpiry {
|
||||||
t.Fatal("retrieved ticket value didnt match expected")
|
t.Fatal("retrieved ticket value didnt match expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
@ -18,7 +19,13 @@ type Ticket struct {
|
|||||||
VoteChoices map[string]string `json:"votechoices"`
|
VoteChoices map[string]string `json:"votechoices"`
|
||||||
VotingKey string `json:"votingkey"`
|
VotingKey string `json:"votingkey"`
|
||||||
VSPFee float64 `json:"vspfee"`
|
VSPFee float64 `json:"vspfee"`
|
||||||
Expiration int64 `json:"expiration"`
|
FeeExpiration int64 `json:"feeexpiration"`
|
||||||
|
FeeTxHash string `json:"feetxhash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Ticket) FeeExpired() bool {
|
||||||
|
now := time.Now()
|
||||||
|
return now.After(time.Unix(t.FeeExpiration, 0))
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -34,6 +41,8 @@ func (vdb *VspDatabase) InsertTicket(ticket Ticket) error {
|
|||||||
return fmt.Errorf("ticket already exists with hash %s", ticket.Hash)
|
return fmt.Errorf("ticket already exists with hash %s", ticket.Hash)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Error if a ticket already exists with the same fee address.
|
||||||
|
|
||||||
ticketBytes, err := json.Marshal(ticket)
|
ticketBytes, err := json.Marshal(ticket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -43,7 +52,7 @@ func (vdb *VspDatabase) InsertTicket(ticket Ticket) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vdb *VspDatabase) SetTicketVotingKey(ticketHash, votingKey string, voteChoices map[string]string) error {
|
func (vdb *VspDatabase) SetTicketVotingKey(ticketHash, votingKey string, voteChoices map[string]string, feeTxHash string) error {
|
||||||
return vdb.db.Update(func(tx *bolt.Tx) error {
|
return vdb.db.Update(func(tx *bolt.Tx) error {
|
||||||
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
|
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
|
||||||
|
|
||||||
@ -62,6 +71,8 @@ func (vdb *VspDatabase) SetTicketVotingKey(ticketHash, votingKey string, voteCho
|
|||||||
|
|
||||||
ticket.VotingKey = votingKey
|
ticket.VotingKey = votingKey
|
||||||
ticket.VoteChoices = voteChoices
|
ticket.VoteChoices = voteChoices
|
||||||
|
ticket.FeeTxHash = feeTxHash
|
||||||
|
|
||||||
ticketBytes, err = json.Marshal(ticket)
|
ticketBytes, err = json.Marshal(ticket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not marshal ticket: %v", err)
|
return fmt.Errorf("could not marshal ticket: %v", err)
|
||||||
@ -133,7 +144,7 @@ func (vdb *VspDatabase) UpdateExpireAndFee(ticketHash string, expiration int64,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("could not unmarshal ticket: %v", err)
|
return fmt.Errorf("could not unmarshal ticket: %v", err)
|
||||||
}
|
}
|
||||||
ticket.Expiration = expiration
|
ticket.FeeExpiration = expiration
|
||||||
ticket.VSPFee = vspFee
|
ticket.VSPFee = vspFee
|
||||||
|
|
||||||
ticketBytes, err = json.Marshal(ticket)
|
ticketBytes, err = json.Marshal(ticket)
|
||||||
|
|||||||
1
main.go
1
main.go
@ -15,7 +15,6 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
feeAccountName = "fees"
|
feeAccountName = "fees"
|
||||||
// TODO: Make expiration configurable?
|
|
||||||
defaultFeeAddressExpiration = 24 * time.Hour
|
defaultFeeAddressExpiration = 24 * time.Hour
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -54,9 +54,9 @@ func feeAddress(c *gin.Context) {
|
|||||||
// Ticket already exists
|
// Ticket already exists
|
||||||
if signature == ticket.CommitmentSignature {
|
if signature == ticket.CommitmentSignature {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
expire := ticket.Expiration
|
expire := ticket.FeeExpiration
|
||||||
VSPFee := ticket.VSPFee
|
VSPFee := ticket.VSPFee
|
||||||
if now.After(time.Unix(ticket.Expiration, 0)) {
|
if ticket.FeeExpired() {
|
||||||
expire = now.Add(cfg.FeeAddressExpiration).Unix()
|
expire = now.Add(cfg.FeeAddressExpiration).Unix()
|
||||||
VSPFee = cfg.VSPFee
|
VSPFee = cfg.VSPFee
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ func feeAddress(c *gin.Context) {
|
|||||||
SDiff: blockHeader.SBits,
|
SDiff: blockHeader.SBits,
|
||||||
BlockHeight: int64(blockHeader.Height),
|
BlockHeight: int64(blockHeader.Height),
|
||||||
VSPFee: cfg.VSPFee,
|
VSPFee: cfg.VSPFee,
|
||||||
Expiration: expire,
|
FeeExpiration: expire,
|
||||||
// VotingKey and VoteChoices: set during payfee
|
// VotingKey and VoteChoices: set during payfee
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -24,8 +24,21 @@ func payFee(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Respond early if the fee tx has already been broadcast for this
|
ticket, err := db.GetTicketByHash(payFeeRequest.TicketHash)
|
||||||
// ticket. Maybe indicate status - mempool/awaiting confs/confirmed.
|
if err != nil {
|
||||||
|
log.Warnf("Invalid ticket from %s", c.ClientIP())
|
||||||
|
sendErrorResponse("invalid ticket", http.StatusBadRequest, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fee transaction has already been broadcast for this ticket.
|
||||||
|
if ticket.FeeTxHash != "" {
|
||||||
|
sendJSONResponse(payFeeResponse{
|
||||||
|
Timestamp: time.Now().Unix(),
|
||||||
|
TxHash: ticket.FeeTxHash,
|
||||||
|
Request: payFeeRequest,
|
||||||
|
}, c)
|
||||||
|
}
|
||||||
|
|
||||||
// Validate VotingKey.
|
// Validate VotingKey.
|
||||||
votingKey := payFeeRequest.VotingKey
|
votingKey := payFeeRequest.VotingKey
|
||||||
@ -70,12 +83,9 @@ func payFee(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: DB - check expiration given during fee address request
|
if ticket.FeeExpired() {
|
||||||
|
log.Warnf("Expired payfee request from %s", c.ClientIP())
|
||||||
ticket, err := db.GetTicketByHash(payFeeRequest.TicketHash)
|
sendErrorResponse("fee has expired", http.StatusBadRequest, c)
|
||||||
if err != nil {
|
|
||||||
log.Warnf("Invalid ticket from %s", c.ClientIP())
|
|
||||||
sendErrorResponse("invalid ticket", http.StatusBadRequest, c)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,20 +162,20 @@ findAddress:
|
|||||||
// pays sufficient fees to the expected address.
|
// pays sufficient fees to the expected address.
|
||||||
// Proceed to update the database and broadcast the transaction.
|
// Proceed to update the database and broadcast the transaction.
|
||||||
|
|
||||||
err = db.SetTicketVotingKey(ticket.Hash, votingWIF.String(), voteChoices)
|
feeTxHash, err := fWalletClient.SendRawTransaction(hex.EncodeToString(feeTxBuf.Bytes()))
|
||||||
if err != nil {
|
|
||||||
log.Errorf("SetTicketVotingKey failed: %v", err)
|
|
||||||
sendErrorResponse("database error", http.StatusInternalServerError, c)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
sendTxHash, err := fWalletClient.SendRawTransaction(hex.EncodeToString(feeTxBuf.Bytes()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Errorf("SendRawTransaction failed: %v", err)
|
log.Errorf("SendRawTransaction failed: %v", err)
|
||||||
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
sendErrorResponse("dcrwallet RPC error", http.StatusInternalServerError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = db.SetTicketVotingKey(ticket.Hash, votingWIF.String(), voteChoices, feeTxHash)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("SetTicketVotingKey failed: %v", err)
|
||||||
|
sendErrorResponse("database error", http.StatusInternalServerError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Should return a response here. We don't want to add the ticket to
|
// TODO: Should return a response here. We don't want to add the ticket to
|
||||||
// the voting wallets until the fee tx has been confirmed.
|
// the voting wallets until the fee tx has been confirmed.
|
||||||
|
|
||||||
@ -216,7 +226,7 @@ findAddress:
|
|||||||
|
|
||||||
sendJSONResponse(payFeeResponse{
|
sendJSONResponse(payFeeResponse{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
TxHash: sendTxHash,
|
TxHash: feeTxHash,
|
||||||
Request: payFeeRequest,
|
Request: payFeeRequest,
|
||||||
}, c)
|
}, c)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -77,6 +77,15 @@ func setVoteChoices(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update VoteChoices in the database before updating the wallets. DB is
|
||||||
|
// source of truth and is less likely to error.
|
||||||
|
err = db.UpdateVoteChoices(txHash.String(), voteChoices)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("UpdateVoteChoices error: %v", err)
|
||||||
|
sendErrorResponse("database error", http.StatusInternalServerError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Update vote choices on voting wallets.
|
// Update vote choices on voting wallets.
|
||||||
for agenda, choice := range voteChoices {
|
for agenda, choice := range voteChoices {
|
||||||
err = vWalletClient.SetVoteChoice(agenda, choice, ticket.Hash)
|
err = vWalletClient.SetVoteChoice(agenda, choice, ticket.Hash)
|
||||||
@ -87,15 +96,6 @@ func setVoteChoices(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Update database before updating wallets. DB is source of truth and
|
|
||||||
// is less likely to error.
|
|
||||||
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 - error if given timestamp is older than any previous requests
|
||||||
|
|
||||||
// TODO: DB - store setvotechoices receipt in log
|
// TODO: DB - store setvotechoices receipt in log
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user