Add missing tests and log errors. (#36)
This commit is contained in:
parent
f50b0aba56
commit
d31c24b531
@ -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`)
|
||||
|
||||
@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
|
||||
@ -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"`
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user