Add missing tests and log errors. (#36)

This commit is contained in:
Jamie Holdstock 2020-05-19 17:21:40 +01:00 committed by GitHub
parent f50b0aba56
commit d31c24b531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 86 additions and 33 deletions

View File

@ -16,6 +16,10 @@
## MVP features
- When dcrvsp is started for the first time, it generates a ed25519 keypair and
stores it in the database. This key is used to sign all API responses, and the
signature is included in the response header `VSP-Signature`. Error responses
are not signed.
- VSP API as described in [dcrstakepool #574](https://github.com/decred/dcrstakepool/issues/574)
- Request fee amount (`GET /fee`)
- Request fee address (`POST /feeaddress`)

View File

@ -22,6 +22,7 @@ func exampleTicket() Ticket {
BlockHeight: 2,
VoteBits: 3,
VotingKey: "VotingKey",
VSPFee: 0.1,
Expiration: 4,
}
}
@ -38,6 +39,8 @@ func TestDatabase(t *testing.T) {
"testGetFeesByFeeAddress": testGetFeesByFeeAddress,
"testInsertFeeAddressVotingKey": testInsertFeeAddressVotingKey,
"testGetInactiveFeeAddresses": testGetInactiveFeeAddresses,
"testUpdateExpireAndFee": testUpdateExpireAndFee,
"testUpdateVoteBits": testUpdateVoteBits,
}
for testName, test := range tests {
@ -106,6 +109,7 @@ func testGetTicketByHash(t *testing.T) {
retrieved.BlockHeight != ticket.BlockHeight ||
retrieved.VoteBits != ticket.VoteBits ||
retrieved.VotingKey != ticket.VotingKey ||
retrieved.VSPFee != ticket.VSPFee ||
retrieved.Expiration != ticket.Expiration {
t.Fatal("retrieved ticket value didnt match expected")
}
@ -217,3 +221,58 @@ func testGetInactiveFeeAddresses(t *testing.T) {
t.Fatal("fee address didnt match expected")
}
}
func testUpdateExpireAndFee(t *testing.T) {
// Insert a ticket into the database.
ticket := exampleTicket()
err := db.InsertFeeAddress(ticket)
if err != nil {
t.Fatalf("error storing ticket in database: %v", err)
}
// Update ticket with new values.
newExpiry := ticket.Expiration + 1
newFee := ticket.VSPFee + 1
err = db.UpdateExpireAndFee(ticket.Hash, newExpiry, newFee)
if err != nil {
t.Fatalf("error updating expiry and fee: %v", err)
}
// Get updated ticket
retrieved, err := db.GetTicketByHash(ticket.Hash)
if err != nil {
t.Fatalf("error retrieving updated ticket: %v", err)
}
// Check ticket fields match expected.
if retrieved.VSPFee != newFee || retrieved.Expiration != newExpiry {
t.Fatal("retrieved ticket value didnt match expected")
}
}
func testUpdateVoteBits(t *testing.T) {
// Insert a ticket into the database.
ticket := exampleTicket()
err := db.InsertFeeAddress(ticket)
if err != nil {
t.Fatalf("error storing ticket in database: %v", err)
}
// Update ticket with new votebits.
newVoteBits := ticket.VoteBits + 1
err = db.UpdateVoteBits(ticket.Hash, newVoteBits)
if err != nil {
t.Fatalf("error updating votebits: %v", err)
}
// Get updated ticket
retrieved, err := db.GetTicketByHash(ticket.Hash)
if err != nil {
t.Fatalf("error retrieving updated ticket: %v", err)
}
// Check ticket fields match expected.
if retrieved.VoteBits != newVoteBits {
t.Fatal("retrieved ticket value didnt match expected")
}
}

View File

@ -150,7 +150,7 @@ func (vdb *VspDatabase) GetTicketByHash(hash string) (Ticket, error) {
}
func (vdb *VspDatabase) UpdateVoteBits(hash string, voteBits uint16) error {
return vdb.db.View(func(tx *bolt.Tx) error {
return vdb.db.Update(func(tx *bolt.Tx) error {
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
key := []byte(hash)
@ -171,19 +171,12 @@ func (vdb *VspDatabase) UpdateVoteBits(hash string, voteBits uint16) error {
return fmt.Errorf("could not marshal ticket: %v", err)
}
// Delete existing ticket
err = ticketBkt.Delete(key)
if err != nil {
return fmt.Errorf("failed to delete ticket: %v", err)
}
// Add updated ticket
return ticketBkt.Put(key, ticketBytes)
})
}
func (vdb *VspDatabase) UpdateExpireAndFee(hash string, expiration int64, vspFee float64) error {
return vdb.db.View(func(tx *bolt.Tx) error {
return vdb.db.Update(func(tx *bolt.Tx) error {
ticketBkt := tx.Bucket(vspBktK).Bucket(ticketBktK)
key := []byte(hash)
@ -205,13 +198,6 @@ func (vdb *VspDatabase) UpdateExpireAndFee(hash string, expiration int64, vspFee
return fmt.Errorf("could not marshal ticket: %v", err)
}
// Delete existing ticket
err = ticketBkt.Delete(key)
if err != nil {
return fmt.Errorf("failed to delete ticket: %v", err)
}
// Add updated ticket
return ticketBkt.Put(key, ticketBytes)
})
}

View File

@ -89,7 +89,8 @@ func feeAddress(c *gin.Context) {
// Check for existing response
ticket, err := db.GetTicketByHash(ticketHashStr)
if err != nil && !errors.Is(err, database.ErrNoTicketFound) {
c.AbortWithError(http.StatusInternalServerError, errors.New("database error"))
log.Errorf("GetTicketByHash error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c)
return
}
if err == nil {
@ -104,7 +105,8 @@ func feeAddress(c *gin.Context) {
err = db.UpdateExpireAndFee(ticketHashStr, expire, VSPFee)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, errors.New("database error"))
log.Errorf("UpdateExpireAndFee error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c)
return
}
}
@ -118,7 +120,8 @@ func feeAddress(c *gin.Context) {
return
}
c.AbortWithError(http.StatusBadRequest, errors.New("invalid signature"))
log.Warnf("Invalid signature from %s", c.ClientIP())
sendErrorResponse("invalid signature", http.StatusBadRequest, c)
return
}
@ -473,7 +476,8 @@ func setVoteBits(c *gin.Context) {
err = db.UpdateVoteBits(txHash.String(), voteBits)
if err != nil {
c.AbortWithError(http.StatusInternalServerError, errors.New("database error"))
log.Errorf("UpdateVoteBits error: %v", err)
sendErrorResponse("database error", http.StatusInternalServerError, c)
return
}

View File

@ -2,7 +2,7 @@ package webapi
type pubKeyResponse struct {
Timestamp int64 `json:"timestamp" binding:"required"`
PubKey []byte `json:"pubKey" binding:"required"`
PubKey []byte `json:"pubkey" binding:"required"`
}
type feeResponse struct {
@ -12,13 +12,13 @@ type feeResponse struct {
type FeeAddressRequest struct {
Timestamp int64 `json:"timestamp" binding:"required"`
TicketHash string `json:"ticketHash" binding:"required"`
TicketHash string `json:"tickethash" binding:"required"`
Signature string `json:"signature" binding:"required"`
}
type feeAddressResponse struct {
Timestamp int64 `json:"timestamp" binding:"required"`
FeeAddress string `json:"feeAddress" binding:"required"`
FeeAddress string `json:"feeaddress" binding:"required"`
Fee float64 `json:"fee" binding:"required"`
Expiration int64 `json:"expiration" binding:"required"`
Request FeeAddressRequest `json:"request" binding:"required"`
@ -26,33 +26,33 @@ type feeAddressResponse struct {
type PayFeeRequest struct {
Timestamp int64 `json:"timestamp" binding:"required"`
Hex string `json:"feeTx" binding:"required"`
VotingKey string `json:"votingKey" binding:"required"`
VoteBits uint16 `json:"voteBits" binding:"required"`
Hex string `json:"feetx" binding:"required"`
VotingKey string `json:"votingkey" binding:"required"`
VoteBits uint16 `json:"votebits" binding:"required"`
}
type payFeeResponse struct {
Timestamp int64 `json:"timestamp" binding:"required"`
TxHash string `json:"txHash" binding:"required"`
TxHash string `json:"txhash" binding:"required"`
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"`
TicketHash string `json:"tickethash" binding:"required"`
Signature string `json:"commitmentsignature" binding:"required"`
VoteBits uint16 `json:"votebits" binding:"required"`
}
type setVoteBitsResponse struct {
Timestamp int64 `json:"timestamp" binding:"required"`
Request SetVoteBitsRequest `json:"request" binding:"required"`
VoteBits uint16 `json:"voteBits" binding:"required"`
VoteBits uint16 `json:"votebits" binding:"required"`
}
type TicketStatusRequest struct {
Timestamp int64 `json:"timestamp" binding:"required"`
TicketHash string `json:"ticketHash" binding:"required"`
TicketHash string `json:"tickethash" binding:"required"`
Signature string `json:"signature" binding:"required"`
}