Add types module for API requests/responses/errors. (#356)
* Remove helper func getCommitmentAddress. This function is only used in one place so removing it does not introduce any duplication. Removing the func also removes the need for errors.Is comparison, which will be very useful in upcoming changes. * Rename apiError to ErrorCode. * Don't use inline type for API errors. * Export webapi errors. * Export webapi request/responses. * Add types module for API requests/responses/errors
This commit is contained in:
parent
a5998264b3
commit
ed1fac1a2a
5
go.mod
5
go.mod
@ -15,6 +15,7 @@ require (
|
|||||||
github.com/decred/dcrd/txscript/v4 v4.0.0
|
github.com/decred/dcrd/txscript/v4 v4.0.0
|
||||||
github.com/decred/dcrd/wire v1.5.0
|
github.com/decred/dcrd/wire v1.5.0
|
||||||
github.com/decred/slog v1.2.0
|
github.com/decred/slog v1.2.0
|
||||||
|
github.com/decred/vspd/types v0.0.0-00010101000000-000000000000
|
||||||
github.com/dustin/go-humanize v1.0.0
|
github.com/dustin/go-humanize v1.0.0
|
||||||
github.com/gin-gonic/gin v1.8.1
|
github.com/gin-gonic/gin v1.8.1
|
||||||
github.com/gorilla/sessions v1.2.1
|
github.com/gorilla/sessions v1.2.1
|
||||||
@ -61,3 +62,7 @@ require (
|
|||||||
google.golang.org/protobuf v1.28.1 // indirect
|
google.golang.org/protobuf v1.28.1 // indirect
|
||||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
|
replace (
|
||||||
|
github.com/decred/vspd/types => ./types
|
||||||
|
)
|
||||||
|
|||||||
33
run_tests.sh
33
run_tests.sh
@ -1,18 +1,39 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
# Copyright (c) 2020-2021 The Decred developers
|
# Copyright (c) 2020-2022 The Decred developers
|
||||||
# Use of this source code is governed by an ISC
|
# Use of this source code is governed by an ISC
|
||||||
# license that can be found in the LICENSE file.
|
# license that can be found in the LICENSE file.
|
||||||
#
|
#
|
||||||
# usage:
|
# usage:
|
||||||
# ./run_tests.sh
|
# ./run_tests.sh
|
||||||
|
|
||||||
set -ex
|
set -e
|
||||||
|
|
||||||
go version
|
go version
|
||||||
|
|
||||||
# run tests
|
# run tests on all modules
|
||||||
env GORACE="halt_on_error=1" go test -race ./...
|
ROOTPATH=$(go list -m)
|
||||||
|
ROOTPATHPATTERN=$(echo $ROOTPATH | sed 's/\\/\\\\/g' | sed 's/\//\\\//g')
|
||||||
|
MODPATHS=$(go list -m all | grep "^$ROOTPATHPATTERN" | cut -d' ' -f1)
|
||||||
|
for module in $MODPATHS; do
|
||||||
|
echo "==> ${module}"
|
||||||
|
env GORACE="halt_on_error=1" go test -race ${module}/...
|
||||||
|
|
||||||
# run linter
|
# check linters
|
||||||
golangci-lint run
|
MODNAME=$(echo $module | sed -E -e "s/^$ROOTPATHPATTERN//" \
|
||||||
|
-e 's,^/,,' -e 's,/v[0-9]+$,,')
|
||||||
|
if [ -z "$MODNAME" ]; then
|
||||||
|
MODNAME=.
|
||||||
|
fi
|
||||||
|
|
||||||
|
# run commands in the module directory as a subshell
|
||||||
|
(
|
||||||
|
cd $MODNAME
|
||||||
|
|
||||||
|
# run linter
|
||||||
|
golangci-lint run
|
||||||
|
)
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "------------------------------------------"
|
||||||
|
echo "Tests completed successfully!"
|
||||||
|
|||||||
120
types/errors.go
Normal file
120
types/errors.go
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
// Copyright (c) 2020-2022 The Decred developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
// ErrorCode is an integer which represents a kind of error which may be
|
||||||
|
// encountered by vspd.
|
||||||
|
type ErrorCode int64
|
||||||
|
|
||||||
|
const (
|
||||||
|
ErrBadRequest ErrorCode = iota
|
||||||
|
ErrInternalError
|
||||||
|
ErrVspClosed
|
||||||
|
ErrFeeAlreadyReceived
|
||||||
|
ErrInvalidFeeTx
|
||||||
|
ErrFeeTooSmall
|
||||||
|
ErrUnknownTicket
|
||||||
|
ErrTicketCannotVote
|
||||||
|
ErrFeeExpired
|
||||||
|
ErrInvalidVoteChoices
|
||||||
|
ErrBadSignature
|
||||||
|
ErrInvalidPrivKey
|
||||||
|
ErrFeeNotReceived
|
||||||
|
ErrInvalidTicket
|
||||||
|
ErrCannotBroadcastTicket
|
||||||
|
ErrCannotBroadcastFee
|
||||||
|
ErrCannotBroadcastFeeUnknownOutputs
|
||||||
|
ErrInvalidTimestamp
|
||||||
|
)
|
||||||
|
|
||||||
|
// HTTPStatus returns a corresponding HTTP status code for a given error code.
|
||||||
|
func (e ErrorCode) HTTPStatus() int {
|
||||||
|
switch e {
|
||||||
|
case ErrBadRequest:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrInternalError:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
case ErrVspClosed:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrFeeAlreadyReceived:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrInvalidFeeTx:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrFeeTooSmall:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrUnknownTicket:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrTicketCannotVote:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrFeeExpired:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrInvalidVoteChoices:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrBadSignature:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrInvalidPrivKey:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrFeeNotReceived:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrInvalidTicket:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
case ErrCannotBroadcastTicket:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
case ErrCannotBroadcastFee:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
case ErrCannotBroadcastFeeUnknownOutputs:
|
||||||
|
return http.StatusPreconditionRequired
|
||||||
|
case ErrInvalidTimestamp:
|
||||||
|
return http.StatusBadRequest
|
||||||
|
default:
|
||||||
|
return http.StatusInternalServerError
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefaultMessage returns a descriptive error string for a given error code.
|
||||||
|
func (e ErrorCode) DefaultMessage() string {
|
||||||
|
switch e {
|
||||||
|
case ErrBadRequest:
|
||||||
|
return "bad request"
|
||||||
|
case ErrInternalError:
|
||||||
|
return "internal error"
|
||||||
|
case ErrVspClosed:
|
||||||
|
return "vsp is closed"
|
||||||
|
case ErrFeeAlreadyReceived:
|
||||||
|
return "fee tx already received for ticket"
|
||||||
|
case ErrInvalidFeeTx:
|
||||||
|
return "invalid fee tx"
|
||||||
|
case ErrFeeTooSmall:
|
||||||
|
return "fee too small"
|
||||||
|
case ErrUnknownTicket:
|
||||||
|
return "unknown ticket"
|
||||||
|
case ErrTicketCannotVote:
|
||||||
|
return "ticket not eligible to vote"
|
||||||
|
case ErrFeeExpired:
|
||||||
|
return "fee has expired"
|
||||||
|
case ErrInvalidVoteChoices:
|
||||||
|
return "invalid vote choices"
|
||||||
|
case ErrBadSignature:
|
||||||
|
return "bad request signature"
|
||||||
|
case ErrInvalidPrivKey:
|
||||||
|
return "invalid private key"
|
||||||
|
case ErrFeeNotReceived:
|
||||||
|
return "no fee tx received for ticket"
|
||||||
|
case ErrInvalidTicket:
|
||||||
|
return "not a valid ticket tx"
|
||||||
|
case ErrCannotBroadcastTicket:
|
||||||
|
return "ticket transaction could not be broadcast"
|
||||||
|
case ErrCannotBroadcastFee:
|
||||||
|
return "fee transaction could not be broadcast"
|
||||||
|
case ErrCannotBroadcastFeeUnknownOutputs:
|
||||||
|
return "fee transaction could not be broadcast due to unknown outputs"
|
||||||
|
case ErrInvalidTimestamp:
|
||||||
|
return "old or reused timestamp"
|
||||||
|
default:
|
||||||
|
return "unknown error"
|
||||||
|
}
|
||||||
|
}
|
||||||
85
types/errors_test.go
Normal file
85
types/errors_test.go
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (c) 2022 The Decred developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestErrorDefaultMessages ensures each ErrorKind can be mapped to a default
|
||||||
|
// descriptive error message.
|
||||||
|
func TestErrorDefaultMessages(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in ErrorCode
|
||||||
|
wantMsg string
|
||||||
|
}{
|
||||||
|
{ErrBadRequest, "bad request"},
|
||||||
|
{ErrInternalError, "internal error"},
|
||||||
|
{ErrVspClosed, "vsp is closed"},
|
||||||
|
{ErrFeeAlreadyReceived, "fee tx already received for ticket"},
|
||||||
|
{ErrInvalidFeeTx, "invalid fee tx"},
|
||||||
|
{ErrFeeTooSmall, "fee too small"},
|
||||||
|
{ErrUnknownTicket, "unknown ticket"},
|
||||||
|
{ErrTicketCannotVote, "ticket not eligible to vote"},
|
||||||
|
{ErrFeeExpired, "fee has expired"},
|
||||||
|
{ErrInvalidVoteChoices, "invalid vote choices"},
|
||||||
|
{ErrBadSignature, "bad request signature"},
|
||||||
|
{ErrInvalidPrivKey, "invalid private key"},
|
||||||
|
{ErrFeeNotReceived, "no fee tx received for ticket"},
|
||||||
|
{ErrInvalidTicket, "not a valid ticket tx"},
|
||||||
|
{ErrCannotBroadcastTicket, "ticket transaction could not be broadcast"},
|
||||||
|
{ErrCannotBroadcastFee, "fee transaction could not be broadcast"},
|
||||||
|
{ErrCannotBroadcastFeeUnknownOutputs, "fee transaction could not be broadcast due to unknown outputs"},
|
||||||
|
{ErrInvalidTimestamp, "old or reused timestamp"},
|
||||||
|
{ErrorCode(9999), "unknown error"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
actualMsg := test.in.DefaultMessage()
|
||||||
|
if actualMsg != test.wantMsg {
|
||||||
|
t.Errorf("wrong default message for ErrorKind(%d). expected: %q actual: %q ",
|
||||||
|
test.in, test.wantMsg, actualMsg)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TestErrorHTTPStatus ensures each ErrorCode can be mapped to a corresponding HTTP status code.
|
||||||
|
func TestErrorHTTPStatus(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
in ErrorCode
|
||||||
|
wantStatus int
|
||||||
|
}{
|
||||||
|
{ErrBadRequest, http.StatusBadRequest},
|
||||||
|
{ErrInternalError, http.StatusInternalServerError},
|
||||||
|
{ErrVspClosed, http.StatusBadRequest},
|
||||||
|
{ErrFeeAlreadyReceived, http.StatusBadRequest},
|
||||||
|
{ErrInvalidFeeTx, http.StatusBadRequest},
|
||||||
|
{ErrFeeTooSmall, http.StatusBadRequest},
|
||||||
|
{ErrUnknownTicket, http.StatusBadRequest},
|
||||||
|
{ErrTicketCannotVote, http.StatusBadRequest},
|
||||||
|
{ErrFeeExpired, http.StatusBadRequest},
|
||||||
|
{ErrInvalidVoteChoices, http.StatusBadRequest},
|
||||||
|
{ErrBadSignature, http.StatusBadRequest},
|
||||||
|
{ErrInvalidPrivKey, http.StatusBadRequest},
|
||||||
|
{ErrFeeNotReceived, http.StatusBadRequest},
|
||||||
|
{ErrInvalidTicket, http.StatusBadRequest},
|
||||||
|
{ErrCannotBroadcastTicket, http.StatusInternalServerError},
|
||||||
|
{ErrCannotBroadcastFee, http.StatusInternalServerError},
|
||||||
|
{ErrCannotBroadcastFeeUnknownOutputs, http.StatusPreconditionRequired},
|
||||||
|
{ErrInvalidTimestamp, http.StatusBadRequest},
|
||||||
|
{ErrorCode(9999), http.StatusInternalServerError},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
result := test.in.HTTPStatus()
|
||||||
|
if result != test.wantStatus {
|
||||||
|
t.Errorf("wrong HTTP status for ErrorKind(%d). expected: %d actual: %d ",
|
||||||
|
test.in, test.wantStatus, result)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
types/go.mod
Normal file
3
types/go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module github.com/decred/vspd/types
|
||||||
|
|
||||||
|
go 1.19
|
||||||
@ -2,9 +2,16 @@
|
|||||||
// Use of this source code is governed by an ISC
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
package webapi
|
package types
|
||||||
|
|
||||||
type vspInfoResponse struct {
|
type APIError struct {
|
||||||
|
Code int64 `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e APIError) Error() string { return e.Message }
|
||||||
|
|
||||||
|
type VspInfoResponse struct {
|
||||||
APIVersions []int64 `json:"apiversions"`
|
APIVersions []int64 `json:"apiversions"`
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
PubKey []byte `json:"pubkey"`
|
PubKey []byte `json:"pubkey"`
|
||||||
@ -22,14 +29,14 @@ type vspInfoResponse struct {
|
|||||||
NetworkProportion float32 `json:"estimatednetworkproportion"`
|
NetworkProportion float32 `json:"estimatednetworkproportion"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type feeAddressRequest struct {
|
type FeeAddressRequest struct {
|
||||||
Timestamp int64 `json:"timestamp" binding:"required"`
|
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
TicketHex string `json:"tickethex" binding:"required"`
|
TicketHex string `json:"tickethex" binding:"required"`
|
||||||
ParentHex string `json:"parenthex" binding:"required"`
|
ParentHex string `json:"parenthex" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type feeAddressResponse struct {
|
type FeeAddressResponse struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
FeeAddress string `json:"feeaddress"`
|
FeeAddress string `json:"feeaddress"`
|
||||||
FeeAmount int64 `json:"feeamount"`
|
FeeAmount int64 `json:"feeamount"`
|
||||||
@ -37,7 +44,7 @@ type feeAddressResponse struct {
|
|||||||
Request []byte `json:"request"`
|
Request []byte `json:"request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type payFeeRequest struct {
|
type PayFeeRequest struct {
|
||||||
Timestamp int64 `json:"timestamp" binding:"required"`
|
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
FeeTx string `json:"feetx" binding:"required"`
|
FeeTx string `json:"feetx" binding:"required"`
|
||||||
@ -47,12 +54,12 @@ type payFeeRequest struct {
|
|||||||
TreasuryPolicy map[string]string `json:"treasurypolicy" binding:"max=3"`
|
TreasuryPolicy map[string]string `json:"treasurypolicy" binding:"max=3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type payFeeResponse struct {
|
type PayFeeResponse struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Request []byte `json:"request"`
|
Request []byte `json:"request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type setVoteChoicesRequest struct {
|
type SetVoteChoicesRequest struct {
|
||||||
Timestamp int64 `json:"timestamp" binding:"required"`
|
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
VoteChoices map[string]string `json:"votechoices" binding:"required"`
|
VoteChoices map[string]string `json:"votechoices" binding:"required"`
|
||||||
@ -60,16 +67,16 @@ type setVoteChoicesRequest struct {
|
|||||||
TreasuryPolicy map[string]string `json:"treasurypolicy" binding:"max=3"`
|
TreasuryPolicy map[string]string `json:"treasurypolicy" binding:"max=3"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type setVoteChoicesResponse struct {
|
type SetVoteChoicesResponse struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Request []byte `json:"request"`
|
Request []byte `json:"request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ticketStatusRequest struct {
|
type TicketStatusRequest struct {
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ticketStatusResponse struct {
|
type TicketStatusResponse struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
TicketConfirmed bool `json:"ticketconfirmed"`
|
TicketConfirmed bool `json:"ticketconfirmed"`
|
||||||
FeeTxStatus string `json:"feetxstatus"`
|
FeeTxStatus string `json:"feetxstatus"`
|
||||||
@ -81,7 +88,7 @@ type ticketStatusResponse struct {
|
|||||||
Request []byte `json:"request"`
|
Request []byte `json:"request"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type setAltSignAddrRequest struct {
|
type SetAltSignAddrRequest struct {
|
||||||
Timestamp int64 `json:"timestamp" binding:"required"`
|
Timestamp int64 `json:"timestamp" binding:"required"`
|
||||||
TicketHash string `json:"tickethash" binding:"required"`
|
TicketHash string `json:"tickethash" binding:"required"`
|
||||||
TicketHex string `json:"tickethex" binding:"required"`
|
TicketHex string `json:"tickethex" binding:"required"`
|
||||||
@ -89,7 +96,7 @@ type setAltSignAddrRequest struct {
|
|||||||
AltSignAddress string `json:"altsignaddress" binding:"required"`
|
AltSignAddress string `json:"altsignaddress" binding:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type setAltSignAddrResponse struct {
|
type SetAltSignAddrResponse struct {
|
||||||
Timestamp int64 `json:"timestamp"`
|
Timestamp int64 `json:"timestamp"`
|
||||||
Request []byte `json:"request"`
|
Request []byte `json:"request"`
|
||||||
}
|
}
|
||||||
53
types/types_test.go
Normal file
53
types/types_test.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
// Copyright (c) 2022 The Decred developers
|
||||||
|
// Use of this source code is governed by an ISC
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestAPIErrorAs ensures APIError can be unwrapped via errors.As.
|
||||||
|
func TestAPIErrorAs(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
testName string
|
||||||
|
apiError error
|
||||||
|
expectedKind ErrorCode
|
||||||
|
expectedMessage string
|
||||||
|
}{{
|
||||||
|
testName: "BadRequest error",
|
||||||
|
apiError: APIError{Message: "something went wrong", Code: int64(ErrBadRequest)},
|
||||||
|
expectedKind: ErrBadRequest,
|
||||||
|
expectedMessage: "something went wrong",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
testName: "Unknown error",
|
||||||
|
apiError: APIError{Message: "something went wrong again", Code: int64(999)},
|
||||||
|
expectedKind: 999,
|
||||||
|
expectedMessage: "something went wrong again",
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
// Ensure APIError can be unwrapped from error.
|
||||||
|
var parsedError APIError
|
||||||
|
if !errors.As(test.apiError, &parsedError) {
|
||||||
|
t.Errorf("%s: unable to unwrap error", test.testName)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsedError.Code != int64(test.expectedKind) {
|
||||||
|
t.Errorf("%s: error was wrong kind. expected: %d actual %d",
|
||||||
|
test.testName, test.expectedKind, parsedError.Code)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if parsedError.Message != test.expectedMessage {
|
||||||
|
t.Errorf("%s: error had wrong message. expected: %q actual %q",
|
||||||
|
test.testName, test.expectedMessage, parsedError.Message)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
125
webapi/errors.go
125
webapi/errors.go
@ -1,125 +0,0 @@
|
|||||||
// Copyright (c) 2020 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package webapi
|
|
||||||
|
|
||||||
import "net/http"
|
|
||||||
|
|
||||||
// apiError is a kind of error. It has full support for errors.Is and
|
|
||||||
// errors.As.
|
|
||||||
type apiError int
|
|
||||||
|
|
||||||
const (
|
|
||||||
errBadRequest apiError = iota
|
|
||||||
errInternalError
|
|
||||||
errVspClosed
|
|
||||||
errFeeAlreadyReceived
|
|
||||||
errInvalidFeeTx
|
|
||||||
errFeeTooSmall
|
|
||||||
errUnknownTicket
|
|
||||||
errTicketCannotVote
|
|
||||||
errFeeExpired
|
|
||||||
errInvalidVoteChoices
|
|
||||||
errBadSignature
|
|
||||||
errInvalidPrivKey
|
|
||||||
errFeeNotReceived
|
|
||||||
errInvalidTicket
|
|
||||||
errCannotBroadcastTicket
|
|
||||||
errCannotBroadcastFee
|
|
||||||
errCannotBroadcastFeeUnknownOutputs
|
|
||||||
errInvalidTimestamp
|
|
||||||
)
|
|
||||||
|
|
||||||
// httpStatus maps application error codes to HTTP status codes.
|
|
||||||
func (e apiError) httpStatus() int {
|
|
||||||
switch e {
|
|
||||||
case errBadRequest:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errInternalError:
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
case errVspClosed:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errFeeAlreadyReceived:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errInvalidFeeTx:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errFeeTooSmall:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errUnknownTicket:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errTicketCannotVote:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errFeeExpired:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errInvalidVoteChoices:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errBadSignature:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errInvalidPrivKey:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errFeeNotReceived:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errInvalidTicket:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
case errCannotBroadcastTicket:
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
case errCannotBroadcastFee:
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
case errCannotBroadcastFeeUnknownOutputs:
|
|
||||||
return http.StatusPreconditionRequired
|
|
||||||
case errInvalidTimestamp:
|
|
||||||
return http.StatusBadRequest
|
|
||||||
default:
|
|
||||||
return http.StatusInternalServerError
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// String returns a descriptive error string for a given error code.
|
|
||||||
func (e apiError) String() string {
|
|
||||||
switch e {
|
|
||||||
case errBadRequest:
|
|
||||||
return "bad request"
|
|
||||||
case errInternalError:
|
|
||||||
return "internal error"
|
|
||||||
case errVspClosed:
|
|
||||||
return "vsp is closed"
|
|
||||||
case errFeeAlreadyReceived:
|
|
||||||
return "fee tx already received for ticket"
|
|
||||||
case errInvalidFeeTx:
|
|
||||||
return "invalid fee tx"
|
|
||||||
case errFeeTooSmall:
|
|
||||||
return "fee too small"
|
|
||||||
case errUnknownTicket:
|
|
||||||
return "unknown ticket"
|
|
||||||
case errTicketCannotVote:
|
|
||||||
return "ticket not eligible to vote"
|
|
||||||
case errFeeExpired:
|
|
||||||
return "fee has expired"
|
|
||||||
case errInvalidVoteChoices:
|
|
||||||
return "invalid vote choices"
|
|
||||||
case errBadSignature:
|
|
||||||
return "bad request signature"
|
|
||||||
case errInvalidPrivKey:
|
|
||||||
return "invalid private key"
|
|
||||||
case errFeeNotReceived:
|
|
||||||
return "no fee tx received for ticket"
|
|
||||||
case errInvalidTicket:
|
|
||||||
return "not a valid ticket tx"
|
|
||||||
case errCannotBroadcastTicket:
|
|
||||||
return "ticket transaction could not be broadcast"
|
|
||||||
case errCannotBroadcastFee:
|
|
||||||
return "fee transaction could not be broadcast"
|
|
||||||
case errCannotBroadcastFeeUnknownOutputs:
|
|
||||||
return "fee transaction could not be broadcast due to unknown outputs"
|
|
||||||
case errInvalidTimestamp:
|
|
||||||
return "old or reused timestamp"
|
|
||||||
default:
|
|
||||||
return "unknown error"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error satisfies the error interface and returns a human-readable error string.
|
|
||||||
func (e apiError) Error() string {
|
|
||||||
return e.String()
|
|
||||||
}
|
|
||||||
@ -1,90 +0,0 @@
|
|||||||
// Copyright (c) 2022 The Decred developers
|
|
||||||
// Use of this source code is governed by an ISC
|
|
||||||
// license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
package webapi
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestApiErrorString tests the stringized output for the apiError type.
|
|
||||||
func TestApiErrorString(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
in apiError
|
|
||||||
want string
|
|
||||||
}{
|
|
||||||
{errBadRequest, "bad request"},
|
|
||||||
{errInternalError, "internal error"},
|
|
||||||
{errVspClosed, "vsp is closed"},
|
|
||||||
{errFeeAlreadyReceived, "fee tx already received for ticket"},
|
|
||||||
{errInvalidFeeTx, "invalid fee tx"},
|
|
||||||
{errFeeTooSmall, "fee too small"},
|
|
||||||
{errUnknownTicket, "unknown ticket"},
|
|
||||||
{errTicketCannotVote, "ticket not eligible to vote"},
|
|
||||||
{errFeeExpired, "fee has expired"},
|
|
||||||
{errInvalidVoteChoices, "invalid vote choices"},
|
|
||||||
{errBadSignature, "bad request signature"},
|
|
||||||
{errInvalidPrivKey, "invalid private key"},
|
|
||||||
{errFeeNotReceived, "no fee tx received for ticket"},
|
|
||||||
{errInvalidTicket, "not a valid ticket tx"},
|
|
||||||
{errCannotBroadcastTicket, "ticket transaction could not be broadcast"},
|
|
||||||
{errCannotBroadcastFee, "fee transaction could not be broadcast"},
|
|
||||||
{errCannotBroadcastFeeUnknownOutputs, "fee transaction could not be broadcast due to unknown outputs"},
|
|
||||||
{errInvalidTimestamp, "old or reused timestamp"},
|
|
||||||
}
|
|
||||||
|
|
||||||
for i, test := range tests {
|
|
||||||
result := test.in.Error()
|
|
||||||
if result != test.want {
|
|
||||||
t.Errorf("%d: got: %s want: %s", i, result, test.want)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TestApiErrorIsAs ensures apiError can be identified via errors.Is and unwrapped via errors.As.
|
|
||||||
func TestApiErrorIsAs(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
err error
|
|
||||||
target error
|
|
||||||
wantMatch bool
|
|
||||||
wantAs apiError
|
|
||||||
}{{
|
|
||||||
name: "errBadRequest == errBadRequest",
|
|
||||||
err: errBadRequest,
|
|
||||||
target: errBadRequest,
|
|
||||||
wantMatch: true,
|
|
||||||
wantAs: errBadRequest,
|
|
||||||
}, {
|
|
||||||
name: "errBadRequest != errInternalError",
|
|
||||||
err: errBadRequest,
|
|
||||||
target: errInternalError,
|
|
||||||
wantMatch: false,
|
|
||||||
wantAs: errBadRequest,
|
|
||||||
}}
|
|
||||||
for _, test := range tests {
|
|
||||||
// Ensure the error matches or not depending on the expected result.
|
|
||||||
result := errors.Is(test.err, test.target)
|
|
||||||
if result != test.wantMatch {
|
|
||||||
t.Errorf("%s: incorrect error identification -- got %v, want %v",
|
|
||||||
test.name, result, test.wantMatch)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the underlying apiError can be unwrapped and is the
|
|
||||||
// expected type.
|
|
||||||
var err apiError
|
|
||||||
if !errors.As(test.err, &err) {
|
|
||||||
t.Errorf("%s: unable to unwrap error", test.name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if err != test.wantAs {
|
|
||||||
t.Errorf("%s: unexpected unwrapped error -- got %v, want %v",
|
|
||||||
test.name, err, test.wantAs)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,6 +12,7 @@ import (
|
|||||||
"github.com/decred/dcrd/dcrutil/v4"
|
"github.com/decred/dcrd/dcrutil/v4"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
@ -84,20 +85,20 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||||
if dcrdErr != nil {
|
if dcrdErr != nil {
|
||||||
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reqBytes := c.MustGet(requestBytesKey).([]byte)
|
reqBytes := c.MustGet(requestBytesKey).([]byte)
|
||||||
|
|
||||||
if s.cfg.VspClosed {
|
if s.cfg.VspClosed {
|
||||||
s.sendError(errVspClosed, c)
|
s.sendError(types.ErrVspClosed, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request feeAddressRequest
|
var request types.FeeAddressRequest
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,7 +111,7 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
ticket.FeeTxStatus == database.FeeConfirmed) {
|
ticket.FeeTxStatus == database.FeeConfirmed) {
|
||||||
s.log.Warnf("%s: Fee tx already received (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Fee tx already received (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticket.Hash)
|
funcName, c.ClientIP(), ticket.Hash)
|
||||||
s.sendError(errFeeAlreadyReceived, c)
|
s.sendError(types.ErrFeeAlreadyReceived, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +119,7 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
rawTicket, err := dcrdClient.GetRawTransaction(ticketHash)
|
rawTicket, err := dcrdClient.GetRawTransaction(ticketHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,13 +127,13 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
canVote, err := canTicketVote(rawTicket, dcrdClient, s.cfg.NetParams)
|
canVote, err := canTicketVote(rawTicket, dcrdClient, s.cfg.NetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: canTicketVote error (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: canTicketVote error (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !canVote {
|
if !canVote {
|
||||||
s.log.Warnf("%s: Unvotable ticket (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Unvotable ticket (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticketHash)
|
funcName, c.ClientIP(), ticketHash)
|
||||||
s.sendError(errTicketCannotVote, c)
|
s.sendError(types.ErrTicketCannotVote, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +146,7 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
newFee, err := s.getCurrentFee(dcrdClient)
|
newFee, err := s.getCurrentFee(dcrdClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: getCurrentFee error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
s.log.Errorf("%s: getCurrentFee error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
ticket.FeeExpiration = now.Add(feeAddressExpiration).Unix()
|
ticket.FeeExpiration = now.Add(feeAddressExpiration).Unix()
|
||||||
@ -155,13 +156,13 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.UpdateTicket error, failed to update fee expiry (ticketHash=%s): %v",
|
s.log.Errorf("%s: db.UpdateTicket error, failed to update fee expiry (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
s.log.Debugf("%s: Expired fee updated (newFeeAmt=%s, ticketHash=%s)",
|
s.log.Debugf("%s: Expired fee updated (newFeeAmt=%s, ticketHash=%s)",
|
||||||
funcName, newFee, ticket.Hash)
|
funcName, newFee, ticket.Hash)
|
||||||
}
|
}
|
||||||
s.sendJSONResponse(feeAddressResponse{
|
s.sendJSONResponse(types.FeeAddressResponse{
|
||||||
Timestamp: now.Unix(),
|
Timestamp: now.Unix(),
|
||||||
Request: reqBytes,
|
Request: reqBytes,
|
||||||
FeeAddress: ticket.FeeAddress,
|
FeeAddress: ticket.FeeAddress,
|
||||||
@ -178,14 +179,14 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
fee, err := s.getCurrentFee(dcrdClient)
|
fee, err := s.getCurrentFee(dcrdClient)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: getCurrentFee error (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: getCurrentFee error (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
newAddress, newAddressIdx, err := s.getNewFeeAddress()
|
newAddress, newAddressIdx, err := s.getNewFeeAddress()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: getNewFeeAddress error (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: getNewFeeAddress error (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
err = s.db.InsertNewTicket(dbTicket)
|
err = s.db.InsertNewTicket(dbTicket)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.InsertNewTicket failed (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: db.InsertNewTicket failed (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -224,7 +225,7 @@ func (s *Server) feeAddress(c *gin.Context) {
|
|||||||
"feeAddr=%s, feeAmt=%s, ticketHash=%s)",
|
"feeAddr=%s, feeAmt=%s, ticketHash=%s)",
|
||||||
funcName, confirmed, newAddressIdx, newAddress, fee, ticketHash)
|
funcName, confirmed, newAddressIdx, newAddress, fee, ticketHash)
|
||||||
|
|
||||||
s.sendJSONResponse(feeAddressResponse{
|
s.sendJSONResponse(types.FeeAddressResponse{
|
||||||
Timestamp: now.Unix(),
|
Timestamp: now.Unix(),
|
||||||
Request: reqBytes,
|
Request: reqBytes,
|
||||||
FeeAddress: newAddress,
|
FeeAddress: newAddress,
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import (
|
|||||||
dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
|
dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
|
||||||
"github.com/decred/dcrd/wire"
|
"github.com/decred/dcrd/wire"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func currentVoteVersion(params *chaincfg.Params) uint32 {
|
func currentVoteVersion(params *chaincfg.Params) uint32 {
|
||||||
@ -170,32 +169,6 @@ func validateTicketHash(hash string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// getCommitmentAddress gets the commitment address of the provided ticket hash
|
|
||||||
// from the chain.
|
|
||||||
func getCommitmentAddress(hash string, dcrdClient *rpc.DcrdRPC, params *chaincfg.Params) (string, error) {
|
|
||||||
resp, err := dcrdClient.GetRawTransaction(hash)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("dcrd.GetRawTransaction for ticket failed: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
msgTx, err := decodeTransaction(resp.Hex)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("failed to decode ticket hex: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = isValidTicket(msgTx)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("invalid ticket: %w", errInvalidTicket)
|
|
||||||
}
|
|
||||||
|
|
||||||
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, params)
|
|
||||||
if err != nil {
|
|
||||||
return "", fmt.Errorf("AddrFromSStxPkScrCommitment error: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return addr.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// canTicketVote checks determines whether a ticket is able to vote at some
|
// canTicketVote checks determines whether a ticket is able to vote at some
|
||||||
// point in the future by checking that it is currently either immature or live.
|
// point in the future by checking that it is currently either immature or live.
|
||||||
func canTicketVote(rawTx *dcrdtypes.TxRawResult, dcrdClient Node, netParams *chaincfg.Params) (bool, error) {
|
func canTicketVote(rawTx *dcrdtypes.TxRawResult, dcrdClient Node, netParams *chaincfg.Params) (bool, error) {
|
||||||
|
|||||||
@ -11,7 +11,9 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/blockchain/stake/v4"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
@ -121,7 +123,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
reqBytes, err := drainAndReplaceBody(c.Request)
|
reqBytes, err := drainAndReplaceBody(c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Error reading request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Error reading request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,7 +135,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,7 +144,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
|
s.log.Errorf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
|
||||||
funcName, request.TicketHash, err)
|
funcName, request.TicketHash, err)
|
||||||
s.sendErrorWithMsg("cannot decode ticket hex", errBadRequest, c)
|
s.sendErrorWithMsg("cannot decode ticket hex", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -150,7 +152,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Invalid ticket (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Invalid ticket (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), request.TicketHash, err)
|
funcName, c.ClientIP(), request.TicketHash, err)
|
||||||
s.sendError(errInvalidTicket, c)
|
s.sendError(types.ErrInvalidTicket, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,7 +160,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
if msgTx.TxHash().String() != request.TicketHash {
|
if msgTx.TxHash().String() != request.TicketHash {
|
||||||
s.log.Warnf("%s: Ticket hex/hash mismatch (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Ticket hex/hash mismatch (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), request.TicketHash)
|
funcName, c.ClientIP(), request.TicketHash)
|
||||||
s.sendErrorWithMsg("ticket hex does not match hash", errBadRequest, c)
|
s.sendErrorWithMsg("ticket hex does not match hash", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +168,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
parentTx, err := decodeTransaction(request.ParentHex)
|
parentTx, err := decodeTransaction(request.ParentHex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Failed to decode parent hex (ticketHash=%s): %v", funcName, request.TicketHash, err)
|
s.log.Errorf("%s: Failed to decode parent hex (ticketHash=%s): %v", funcName, request.TicketHash, err)
|
||||||
s.sendErrorWithMsg("cannot decode parent hex", errBadRequest, c)
|
s.sendErrorWithMsg("cannot decode parent hex", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
parentHash := parentTx.TxHash()
|
parentHash := parentTx.TxHash()
|
||||||
@ -176,7 +178,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||||
if dcrdErr != nil {
|
if dcrdErr != nil {
|
||||||
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -201,7 +203,7 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
|
|
||||||
if !found {
|
if !found {
|
||||||
s.log.Errorf("%s: Invalid ticket parent (ticketHash=%s)", funcName, request.TicketHash)
|
s.log.Errorf("%s: Invalid ticket parent (ticketHash=%s)", funcName, request.TicketHash)
|
||||||
s.sendErrorWithMsg("invalid ticket parent", errBadRequest, c)
|
s.sendErrorWithMsg("invalid ticket parent", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,14 +212,14 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: dcrd.SendRawTransaction for parent tx failed (ticketHash=%s): %v",
|
s.log.Errorf("%s: dcrd.SendRawTransaction for parent tx failed (ticketHash=%s): %v",
|
||||||
funcName, request.TicketHash, err)
|
funcName, request.TicketHash, err)
|
||||||
s.sendError(errCannotBroadcastTicket, c)
|
s.sendError(types.ErrCannotBroadcastTicket, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket parent failed (ticketHash=%s): %v",
|
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket parent failed (ticketHash=%s): %v",
|
||||||
funcName, request.TicketHash, err)
|
funcName, request.TicketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -236,13 +238,13 @@ func (s *Server) broadcastTicket(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: dcrd.SendRawTransaction for ticket failed (ticketHash=%s): %v",
|
s.log.Errorf("%s: dcrd.SendRawTransaction for ticket failed (ticketHash=%s): %v",
|
||||||
funcName, request.TicketHash, err)
|
funcName, request.TicketHash, err)
|
||||||
s.sendError(errCannotBroadcastTicket, c)
|
s.sendError(types.ErrCannotBroadcastTicket, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v",
|
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v",
|
||||||
funcName, request.TicketHash, err)
|
funcName, request.TicketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -261,7 +263,7 @@ func (s *Server) vspAuth(c *gin.Context) {
|
|||||||
reqBytes, err := drainAndReplaceBody(c.Request)
|
reqBytes, err := drainAndReplaceBody(c.Request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Error reading request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Error reading request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -275,7 +277,7 @@ func (s *Server) vspAuth(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
hash := request.TicketHash
|
hash := request.TicketHash
|
||||||
@ -284,7 +286,7 @@ func (s *Server) vspAuth(c *gin.Context) {
|
|||||||
err = validateTicketHash(hash)
|
err = validateTicketHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Errorf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg("invalid ticket hash", errBadRequest, c)
|
s.sendErrorWithMsg("invalid ticket hash", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,45 +294,67 @@ func (s *Server) vspAuth(c *gin.Context) {
|
|||||||
ticket, ticketFound, err := s.db.GetTicketByHash(hash)
|
ticket, ticketFound, err := s.db.GetTicketByHash(hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.GetTicketByHash error (ticketHash=%s): %v", funcName, hash, err)
|
s.log.Errorf("%s: db.GetTicketByHash error (ticketHash=%s): %v", funcName, hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the ticket was found in the database, we already know its
|
|
||||||
// commitment address. Otherwise we need to get it from the chain.
|
|
||||||
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
|
|
||||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
|
||||||
if dcrdErr != nil {
|
|
||||||
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
|
||||||
s.sendError(errInternalError, c)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var commitmentAddress string
|
var commitmentAddress string
|
||||||
if ticketFound {
|
if ticketFound {
|
||||||
|
// The commitment address is already known if the ticket already exists
|
||||||
|
// in the database.
|
||||||
commitmentAddress = ticket.CommitmentAddress
|
commitmentAddress = ticket.CommitmentAddress
|
||||||
} else {
|
} else {
|
||||||
commitmentAddress, err = getCommitmentAddress(hash, dcrdClient, s.cfg.NetParams)
|
// Otherwise the commitment address must be retrieved from the chain
|
||||||
if err != nil {
|
// using dcrd.
|
||||||
s.log.Errorf("%s: Failed to get commitment address (clientIP=%s, ticketHash=%s): %v",
|
dcrdClient := c.MustGet(dcrdKey).(*rpc.DcrdRPC)
|
||||||
funcName, c.ClientIP(), hash, err)
|
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||||
|
if dcrdErr != nil {
|
||||||
var apiErr *apiError
|
s.log.Errorf("%s: Could not get dcrd client (clientIP=%s, ticketHash=%s): %v",
|
||||||
if errors.Is(err, apiErr) {
|
funcName, c.ClientIP(), hash, dcrdErr.(error))
|
||||||
s.sendError(errInvalidTicket, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
} else {
|
|
||||||
s.sendError(errInternalError, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rawTx, err := dcrdClient.GetRawTransaction(hash)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (clientIP=%s, ticketHash=%s): %v",
|
||||||
|
funcName, c.ClientIP(), hash, err)
|
||||||
|
s.sendError(types.ErrInternalError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
msgTx, err := decodeTransaction(rawTx.Hex)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorf("%s: Failed to decode ticket hex (clientIP=%s, ticketHash=%s): %v",
|
||||||
|
funcName, c.ClientIP(), hash, err)
|
||||||
|
s.sendError(types.ErrInternalError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err = isValidTicket(msgTx)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorf("%s: Invalid ticket (clientIP=%s, ticketHash=%s)",
|
||||||
|
funcName, c.ClientIP(), hash)
|
||||||
|
s.sendError(types.ErrInvalidTicket, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
addr, err := stake.AddrFromSStxPkScrCommitment(msgTx.TxOut[1].PkScript, s.cfg.NetParams)
|
||||||
|
if err != nil {
|
||||||
|
s.log.Errorf("%s: AddrFromSStxPkScrCommitment error (clientIP=%s, ticketHash=%s): %v",
|
||||||
|
funcName, c.ClientIP(), hash, err)
|
||||||
|
s.sendError(types.ErrInternalError, c)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
commitmentAddress = addr.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure a signature is provided.
|
// Ensure a signature is provided.
|
||||||
signature := c.GetHeader("VSP-Client-Signature")
|
signature := c.GetHeader("VSP-Client-Signature")
|
||||||
if signature == "" {
|
if signature == "" {
|
||||||
s.log.Warnf("%s: No VSP-Client-Signature header (clientIP=%s)", funcName, c.ClientIP())
|
s.log.Warnf("%s: No VSP-Client-Signature header (clientIP=%s)", funcName, c.ClientIP())
|
||||||
s.sendErrorWithMsg("no VSP-Client-Signature header", errBadRequest, c)
|
s.sendErrorWithMsg("no VSP-Client-Signature header", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -339,7 +363,7 @@ func (s *Server) vspAuth(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Couldn't validate signature (clientIP=%s, ticketHash=%s): %v",
|
s.log.Errorf("%s: Couldn't validate signature (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), hash, err)
|
funcName, c.ClientIP(), hash, err)
|
||||||
s.sendError(errBadSignature, c)
|
s.sendError(types.ErrBadSignature, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
@ -30,26 +31,26 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||||
if dcrdErr != nil {
|
if dcrdErr != nil {
|
||||||
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reqBytes := c.MustGet(requestBytesKey).([]byte)
|
reqBytes := c.MustGet(requestBytesKey).([]byte)
|
||||||
|
|
||||||
if s.cfg.VspClosed {
|
if s.cfg.VspClosed {
|
||||||
s.sendError(errVspClosed, c)
|
s.sendError(types.ErrVspClosed, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !knownTicket {
|
if !knownTicket {
|
||||||
s.log.Warnf("%s: Unknown ticket (clientIP=%s)", funcName, c.ClientIP())
|
s.log.Warnf("%s: Unknown ticket (clientIP=%s)", funcName, c.ClientIP())
|
||||||
s.sendError(errUnknownTicket, c)
|
s.sendError(types.ErrUnknownTicket, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request payFeeRequest
|
var request types.PayFeeRequest
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,7 +60,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
ticket.FeeTxStatus == database.FeeConfirmed {
|
ticket.FeeTxStatus == database.FeeConfirmed {
|
||||||
s.log.Warnf("%s: Fee tx already received (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Fee tx already received (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticket.Hash)
|
funcName, c.ClientIP(), ticket.Hash)
|
||||||
s.sendError(errFeeAlreadyReceived, c)
|
s.sendError(types.ErrFeeAlreadyReceived, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
rawTicket, err := dcrdClient.GetRawTransaction(ticket.Hash)
|
rawTicket, err := dcrdClient.GetRawTransaction(ticket.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,13 +76,13 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
canVote, err := canTicketVote(rawTicket, dcrdClient, s.cfg.NetParams)
|
canVote, err := canTicketVote(rawTicket, dcrdClient, s.cfg.NetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: canTicketVote error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
s.log.Errorf("%s: canTicketVote error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !canVote {
|
if !canVote {
|
||||||
s.log.Warnf("%s: Unvotable ticket (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Unvotable ticket (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticket.Hash)
|
funcName, c.ClientIP(), ticket.Hash)
|
||||||
s.sendError(errTicketCannotVote, c)
|
s.sendError(types.ErrTicketCannotVote, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -89,7 +90,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if ticket.FeeExpired() {
|
if ticket.FeeExpired() {
|
||||||
s.log.Warnf("%s: Expired payfee request (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Expired payfee request (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticket.Hash)
|
funcName, c.ClientIP(), ticket.Hash)
|
||||||
s.sendError(errFeeExpired, c)
|
s.sendError(types.ErrFeeExpired, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +100,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Failed to decode WIF (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Failed to decode WIF (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, c.ClientIP(), ticket.Hash, err)
|
||||||
s.sendError(errInvalidPrivKey, c)
|
s.sendError(types.ErrInvalidPrivKey, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -135,7 +136,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Failed to decode fee tx hex (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Failed to decode fee tx hex (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, c.ClientIP(), ticket.Hash, err)
|
||||||
s.sendError(errInvalidFeeTx, c)
|
s.sendError(types.ErrInvalidFeeTx, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -143,7 +144,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Fee tx failed sanity check (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Fee tx failed sanity check (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, c.ClientIP(), ticket.Hash, err)
|
||||||
s.sendError(errInvalidFeeTx, c)
|
s.sendError(types.ErrInvalidFeeTx, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,7 +153,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Failed to decode fee address (ticketHash=%s): %v",
|
s.log.Errorf("%s: Failed to decode fee address (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +175,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
funcName, ticket.Hash, ticket.FeeAddress, c.ClientIP())
|
funcName, ticket.Hash, ticket.FeeAddress, c.ClientIP())
|
||||||
s.sendErrorWithMsg(
|
s.sendErrorWithMsg(
|
||||||
fmt.Sprintf("feetx did not include any payments for fee address %s", ticket.FeeAddress),
|
fmt.Sprintf("feetx did not include any payments for fee address %s", ticket.FeeAddress),
|
||||||
errInvalidFeeTx, c)
|
types.ErrInvalidFeeTx, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -183,7 +184,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if feePaid < minFee {
|
if feePaid < minFee {
|
||||||
s.log.Warnf("%s: Fee too small (ticketHash=%s, clientIP=%s): was %s, expected minimum %s",
|
s.log.Warnf("%s: Fee too small (ticketHash=%s, clientIP=%s): was %s, expected minimum %s",
|
||||||
funcName, ticket.Hash, c.ClientIP(), feePaid, minFee)
|
funcName, ticket.Hash, c.ClientIP(), feePaid, minFee)
|
||||||
s.sendError(errFeeTooSmall, c)
|
s.sendError(types.ErrFeeTooSmall, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,7 +194,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Failed to get voting address from WIF (ticketHash=%s, clientIP=%s): %v",
|
s.log.Errorf("%s: Failed to get voting address from WIF (ticketHash=%s, clientIP=%s): %v",
|
||||||
funcName, ticket.Hash, c.ClientIP(), err)
|
funcName, ticket.Hash, c.ClientIP(), err)
|
||||||
s.sendError(errInvalidPrivKey, c)
|
s.sendError(types.ErrInvalidPrivKey, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -204,7 +205,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
|
s.log.Warnf("%s: Failed to decode ticket hex (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,7 +218,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
s.log.Warnf("%s: Voting address does not match provided private key: (ticketHash=%s)",
|
s.log.Warnf("%s: Voting address does not match provided private key: (ticketHash=%s)",
|
||||||
funcName, ticket.Hash)
|
funcName, ticket.Hash)
|
||||||
s.sendErrorWithMsg("voting address does not match provided private key",
|
s.sendErrorWithMsg("voting address does not match provided private key",
|
||||||
errInvalidPrivKey, c)
|
types.ErrInvalidPrivKey, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +247,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.UpdateTicket error, failed to set fee tx (ticketHash=%s): %v",
|
s.log.Errorf("%s: db.UpdateTicket error, failed to set fee tx (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -263,9 +264,9 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
|
|
||||||
if strings.Contains(err.Error(),
|
if strings.Contains(err.Error(),
|
||||||
"references outputs of unknown or fully-spent transaction") {
|
"references outputs of unknown or fully-spent transaction") {
|
||||||
s.sendError(errCannotBroadcastFeeUnknownOutputs, c)
|
s.sendError(types.ErrCannotBroadcastFeeUnknownOutputs, c)
|
||||||
} else {
|
} else {
|
||||||
s.sendError(errCannotBroadcastFee, c)
|
s.sendError(types.ErrCannotBroadcastFee, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.db.UpdateTicket(ticket)
|
err = s.db.UpdateTicket(ticket)
|
||||||
@ -283,7 +284,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.UpdateTicket error, failed to set fee tx as broadcast (ticketHash=%s): %v",
|
s.log.Errorf("%s: db.UpdateTicket error, failed to set fee tx as broadcast (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,7 +293,7 @@ func (s *Server) payFee(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send success response to client.
|
// Send success response to client.
|
||||||
resp, respSig := s.sendJSONResponse(payFeeResponse{
|
resp, respSig := s.sendJSONResponse(types.PayFeeResponse{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Request: reqBytes,
|
Request: reqBytes,
|
||||||
}, c)
|
}, c)
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
@ -34,20 +35,20 @@ func (s *Server) setAltSignAddr(c *gin.Context) {
|
|||||||
dcrdErr := c.MustGet(dcrdErrorKey)
|
dcrdErr := c.MustGet(dcrdErrorKey)
|
||||||
if dcrdErr != nil {
|
if dcrdErr != nil {
|
||||||
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
s.log.Errorf("%s: Could not get dcrd client: %v", funcName, dcrdErr.(error))
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
reqBytes := c.MustGet(requestBytesKey).([]byte)
|
reqBytes := c.MustGet(requestBytesKey).([]byte)
|
||||||
|
|
||||||
if s.cfg.VspClosed {
|
if s.cfg.VspClosed {
|
||||||
s.sendError(errVspClosed, c)
|
s.sendError(types.ErrVspClosed, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request setAltSignAddrRequest
|
var request types.SetAltSignAddrRequest
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,13 +57,13 @@ func (s *Server) setAltSignAddr(c *gin.Context) {
|
|||||||
currentData, err := s.db.AltSignAddrData(ticketHash)
|
currentData, err := s.db.AltSignAddrData(ticketHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.AltSignAddrData (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: db.AltSignAddrData (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if currentData != nil {
|
if currentData != nil {
|
||||||
msg := "alternate sign address data already exists"
|
msg := "alternate sign address data already exists"
|
||||||
s.log.Warnf("%s: %s (ticketHash=%s)", funcName, msg, ticketHash)
|
s.log.Warnf("%s: %s (ticketHash=%s)", funcName, msg, ticketHash)
|
||||||
s.sendErrorWithMsg(msg, errBadRequest, c)
|
s.sendErrorWithMsg(msg, types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -71,12 +72,12 @@ func (s *Server) setAltSignAddr(c *gin.Context) {
|
|||||||
addr, err := stdaddr.DecodeAddressV0(altSignAddr, s.cfg.NetParams)
|
addr, err := stdaddr.DecodeAddressV0(altSignAddr, s.cfg.NetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Alt sign address cannot be decoded (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Alt sign address cannot be decoded (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if _, ok := addr.(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0); !ok {
|
if _, ok := addr.(*stdaddr.AddressPubKeyHashEcdsaSecp256k1V0); !ok {
|
||||||
s.log.Warnf("%s: Alt sign address is unexpected type (clientIP=%s, type=%T)", funcName, c.ClientIP(), addr)
|
s.log.Warnf("%s: Alt sign address is unexpected type (clientIP=%s, type=%T)", funcName, c.ClientIP(), addr)
|
||||||
s.sendErrorWithMsg("wrong type for alternate signing address", errBadRequest, c)
|
s.sendErrorWithMsg("wrong type for alternate signing address", types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +85,7 @@ func (s *Server) setAltSignAddr(c *gin.Context) {
|
|||||||
rawTicket, err := dcrdClient.GetRawTransaction(ticketHash)
|
rawTicket, err := dcrdClient.GetRawTransaction(ticketHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: dcrd.GetRawTransaction for ticket failed (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,18 +93,18 @@ func (s *Server) setAltSignAddr(c *gin.Context) {
|
|||||||
canVote, err := canTicketVote(rawTicket, dcrdClient, s.cfg.NetParams)
|
canVote, err := canTicketVote(rawTicket, dcrdClient, s.cfg.NetParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: canTicketVote error (ticketHash=%s): %v", funcName, ticketHash, err)
|
s.log.Errorf("%s: canTicketVote error (ticketHash=%s): %v", funcName, ticketHash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !canVote {
|
if !canVote {
|
||||||
s.log.Warnf("%s: unvotable ticket (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: unvotable ticket (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticketHash)
|
funcName, c.ClientIP(), ticketHash)
|
||||||
s.sendError(errTicketCannotVote, c)
|
s.sendError(types.ErrTicketCannotVote, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send success response to client.
|
// Send success response to client.
|
||||||
resp, respSig := s.sendJSONResponse(setAltSignAddrResponse{
|
resp, respSig := s.sendJSONResponse(types.SetAltSignAddrResponse{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Request: reqBytes,
|
Request: reqBytes,
|
||||||
}, c)
|
}, c)
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import (
|
|||||||
dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
|
dcrdtypes "github.com/decred/dcrd/rpc/jsonrpc/types/v3"
|
||||||
"github.com/decred/slog"
|
"github.com/decred/slog"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -211,7 +212,7 @@ func TestSetAltSignAddress(t *testing.T) {
|
|||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
ticketHash := randString(64, hexCharset)
|
ticketHash := randString(64, hexCharset)
|
||||||
req := &setAltSignAddrRequest{
|
req := &types.SetAltSignAddrRequest{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
TicketHash: ticketHash,
|
TicketHash: ticketHash,
|
||||||
TicketHex: randString(504, hexCharset),
|
TicketHex: randString(504, hexCharset),
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import (
|
|||||||
|
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
@ -28,20 +29,20 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
// If we cannot set the vote choices on at least one voting wallet right
|
// If we cannot set the vote choices on at least one voting wallet right
|
||||||
// now, don't update the database, just return an error.
|
// now, don't update the database, just return an error.
|
||||||
if len(walletClients) == 0 {
|
if len(walletClients) == 0 {
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !knownTicket {
|
if !knownTicket {
|
||||||
s.log.Warnf("%s: Unknown ticket (clientIP=%s)", funcName, c.ClientIP())
|
s.log.Warnf("%s: Unknown ticket (clientIP=%s)", funcName, c.ClientIP())
|
||||||
s.sendError(errUnknownTicket, c)
|
s.sendError(types.ErrUnknownTicket, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if ticket.FeeTxStatus == database.NoFee {
|
if ticket.FeeTxStatus == database.NoFee {
|
||||||
s.log.Warnf("%s: No fee tx for ticket (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: No fee tx for ticket (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticket.Hash)
|
funcName, c.ClientIP(), ticket.Hash)
|
||||||
s.sendError(errFeeNotReceived, c)
|
s.sendError(types.ErrFeeNotReceived, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,14 +51,14 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
s.log.Warnf("%s: Ticket not eligible to vote (clientIP=%s, ticketHash=%s)",
|
s.log.Warnf("%s: Ticket not eligible to vote (clientIP=%s, ticketHash=%s)",
|
||||||
funcName, c.ClientIP(), ticket.Hash)
|
funcName, c.ClientIP(), ticket.Hash)
|
||||||
s.sendErrorWithMsg(fmt.Sprintf("ticket not eligible to vote (status=%s)", ticket.Outcome),
|
s.sendErrorWithMsg(fmt.Sprintf("ticket not eligible to vote (status=%s)", ticket.Outcome),
|
||||||
errTicketCannotVote, c)
|
types.ErrTicketCannotVote, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request setVoteChoicesRequest
|
var request types.SetVoteChoicesRequest
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -67,7 +68,7 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.GetVoteChanges error (ticketHash=%s): %v",
|
s.log.Errorf("%s: db.GetVoteChanges error (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: Could not unmarshal vote change record (ticketHash=%s): %v",
|
s.log.Errorf("%s: Could not unmarshal vote change record (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
s.log.Warnf("%s: Request uses invalid timestamp, %d is not greater "+
|
s.log.Warnf("%s: Request uses invalid timestamp, %d is not greater "+
|
||||||
"than %d (ticketHash=%s)",
|
"than %d (ticketHash=%s)",
|
||||||
funcName, request.Timestamp, prevReq.Timestamp, ticket.Hash)
|
funcName, request.Timestamp, prevReq.Timestamp, ticket.Hash)
|
||||||
s.sendError(errInvalidTimestamp, c)
|
s.sendError(types.ErrInvalidTimestamp, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -98,7 +99,7 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Invalid consensus vote choices (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Invalid consensus vote choices (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, c.ClientIP(), ticket.Hash, err)
|
||||||
s.sendErrorWithMsg(err.Error(), errInvalidVoteChoices, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrInvalidVoteChoices, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,14 +107,14 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Invalid treasury policy (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Invalid treasury policy (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, c.ClientIP(), ticket.Hash, err)
|
||||||
s.sendErrorWithMsg(err.Error(), errInvalidVoteChoices, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrInvalidVoteChoices, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = validTSpendPolicy(request.TSpendPolicy)
|
err = validTSpendPolicy(request.TSpendPolicy)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Warnf("%s: Invalid tspend policy (clientIP=%s, ticketHash=%s): %v",
|
s.log.Warnf("%s: Invalid tspend policy (clientIP=%s, ticketHash=%s): %v",
|
||||||
funcName, c.ClientIP(), ticket.Hash, err)
|
funcName, c.ClientIP(), ticket.Hash, err)
|
||||||
s.sendErrorWithMsg(err.Error(), errInvalidVoteChoices, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrInvalidVoteChoices, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update voting preferences in the database before updating the wallets. DB
|
// Update voting preferences in the database before updating the wallets. DB
|
||||||
@ -135,7 +136,7 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.UpdateTicket error, failed to set consensus vote choices (ticketHash=%s): %v",
|
s.log.Errorf("%s: db.UpdateTicket error, failed to set consensus vote choices (ticketHash=%s): %v",
|
||||||
funcName, ticket.Hash, err)
|
funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +180,7 @@ func (s *Server) setVoteChoices(c *gin.Context) {
|
|||||||
s.log.Debugf("%s: Vote choices updated (ticketHash=%s)", funcName, ticket.Hash)
|
s.log.Debugf("%s: Vote choices updated (ticketHash=%s)", funcName, ticket.Hash)
|
||||||
|
|
||||||
// Send success response to client.
|
// Send success response to client.
|
||||||
resp, respSig := s.sendJSONResponse(setVoteChoicesResponse{
|
resp, respSig := s.sendJSONResponse(types.SetVoteChoicesResponse{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Request: reqBytes,
|
Request: reqBytes,
|
||||||
}, c)
|
}, c)
|
||||||
|
|||||||
@ -8,6 +8,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gin-gonic/gin/binding"
|
"github.com/gin-gonic/gin/binding"
|
||||||
)
|
)
|
||||||
@ -23,14 +24,14 @@ func (s *Server) ticketStatus(c *gin.Context) {
|
|||||||
|
|
||||||
if !knownTicket {
|
if !knownTicket {
|
||||||
s.log.Warnf("%s: Unknown ticket (clientIP=%s)", funcName, c.ClientIP())
|
s.log.Warnf("%s: Unknown ticket (clientIP=%s)", funcName, c.ClientIP())
|
||||||
s.sendError(errUnknownTicket, c)
|
s.sendError(types.ErrUnknownTicket, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var request ticketStatusRequest
|
var request types.TicketStatusRequest
|
||||||
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
if err := binding.JSON.BindBody(reqBytes, &request); err != nil {
|
||||||
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
s.log.Warnf("%s: Bad request (clientIP=%s): %v", funcName, c.ClientIP(), err)
|
||||||
s.sendErrorWithMsg(err.Error(), errBadRequest, c)
|
s.sendErrorWithMsg(err.Error(), types.ErrBadRequest, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -38,7 +39,7 @@ func (s *Server) ticketStatus(c *gin.Context) {
|
|||||||
altSignAddrData, err := s.db.AltSignAddrData(ticket.Hash)
|
altSignAddrData, err := s.db.AltSignAddrData(ticket.Hash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("%s: db.AltSignAddrData error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
s.log.Errorf("%s: db.AltSignAddrData error (ticketHash=%s): %v", funcName, ticket.Hash, err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ func (s *Server) ticketStatus(c *gin.Context) {
|
|||||||
altSignAddr = altSignAddrData.AltSignAddr
|
altSignAddr = altSignAddrData.AltSignAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
s.sendJSONResponse(ticketStatusResponse{
|
s.sendJSONResponse(types.TicketStatusResponse{
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
Request: reqBytes,
|
Request: reqBytes,
|
||||||
TicketConfirmed: ticket.Confirmed,
|
TicketConfirmed: ticket.Confirmed,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ package webapi
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/decred/vspd/version"
|
"github.com/decred/vspd/version"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
)
|
)
|
||||||
@ -14,7 +15,7 @@ import (
|
|||||||
// vspInfo is the handler for "GET /api/v3/vspinfo".
|
// vspInfo is the handler for "GET /api/v3/vspinfo".
|
||||||
func (s *Server) vspInfo(c *gin.Context) {
|
func (s *Server) vspInfo(c *gin.Context) {
|
||||||
cachedStats := s.cache.getData()
|
cachedStats := s.cache.getData()
|
||||||
s.sendJSONResponse(vspInfoResponse{
|
s.sendJSONResponse(types.VspInfoResponse{
|
||||||
APIVersions: []int64{3},
|
APIVersions: []int64{3},
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
PubKey: s.signPubKey,
|
PubKey: s.signPubKey,
|
||||||
|
|||||||
@ -21,6 +21,7 @@ import (
|
|||||||
"github.com/decred/slog"
|
"github.com/decred/slog"
|
||||||
"github.com/decred/vspd/database"
|
"github.com/decred/vspd/database"
|
||||||
"github.com/decred/vspd/rpc"
|
"github.com/decred/vspd/rpc"
|
||||||
|
"github.com/decred/vspd/types"
|
||||||
"github.com/dustin/go-humanize"
|
"github.com/dustin/go-humanize"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
@ -276,7 +277,7 @@ func (s *Server) sendJSONResponse(resp interface{}, c *gin.Context) (string, str
|
|||||||
dec, err := json.Marshal(resp)
|
dec, err := json.Marshal(resp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.log.Errorf("JSON marshal error: %v", err)
|
s.log.Errorf("JSON marshal error: %v", err)
|
||||||
s.sendError(errInternalError, c)
|
s.sendError(types.ErrInternalError, c)
|
||||||
return "", ""
|
return "", ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,21 +290,21 @@ func (s *Server) sendJSONResponse(resp interface{}, c *gin.Context) (string, str
|
|||||||
return string(dec), sigStr
|
return string(dec), sigStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendError sends an error response to the client using the default error
|
// sendError sends an error response with the provided error code and the
|
||||||
// message.
|
// default message for that code.
|
||||||
func (s *Server) sendError(e apiError, c *gin.Context) {
|
func (s *Server) sendError(e types.ErrorCode, c *gin.Context) {
|
||||||
msg := e.Error()
|
msg := e.DefaultMessage()
|
||||||
s.sendErrorWithMsg(msg, e, c)
|
s.sendErrorWithMsg(msg, e, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
// sendErrorWithMsg sends an error response to the client using the provided
|
// sendErrorWithMsg sends an error response with the provided error code and
|
||||||
// error message.
|
// message.
|
||||||
func (s *Server) sendErrorWithMsg(msg string, e apiError, c *gin.Context) {
|
func (s *Server) sendErrorWithMsg(msg string, e types.ErrorCode, c *gin.Context) {
|
||||||
status := e.httpStatus()
|
status := e.HTTPStatus()
|
||||||
|
|
||||||
resp := gin.H{
|
resp := types.APIError{
|
||||||
"code": int(e),
|
Code: int64(e),
|
||||||
"message": msg,
|
Message: msg,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to sign the error response. If it fails, send it without a signature.
|
// Try to sign the error response. If it fails, send it without a signature.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user