Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

secp256k1/schnorr: update error types. #2282

Merged
merged 1 commit into from
Dec 17, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 27 additions & 92 deletions dcrec/secp256k1/schnorr/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,146 +5,81 @@

package schnorr

import (
"fmt"
)

// ErrorCode identifies a kind of signature-related error. It has full support
// for errors.Is and errors.As, so the caller can directly check against an
// error code when determining the reason for an error.
type ErrorCode int
// ErrorKind identifies a kind of error. It has full support for errors.Is
// and errors.As, so the caller can directly check against an error kind
// when determining the reason for an error.
type ErrorKind string

// These constants are used to identify a specific RuleError.
const (
// ErrInvalidHashLen indicates that the input hash to sign or verify is not
// the required length.
ErrInvalidHashLen ErrorCode = iota
ErrInvalidHashLen = ErrorKind("ErrInvalidHashLen")

// ErrPrivateKeyIsZero indicates an attempt was made to sign a message with
// a private key that is equal to zero.
ErrPrivateKeyIsZero
ErrPrivateKeyIsZero = ErrorKind("ErrPrivateKeyIsZero")

// ErrSchnorrHashValue indicates that the hash of (R || m) was too large and
// so a new nonce should be used.
ErrSchnorrHashValue
ErrSchnorrHashValue = ErrorKind("ErrSchnorrHashValue")

// ErrPubKeyNotOnCurve indicates that a point was not on the given elliptic
// curve.
ErrPubKeyNotOnCurve
ErrPubKeyNotOnCurve = ErrorKind("ErrPubKeyNotOnCurve")

// ErrSigRYIsOdd indicates that the calculated Y value of R was odd.
ErrSigRYIsOdd
ErrSigRYIsOdd = ErrorKind("ErrSigRYIsOdd")

// ErrSigRNotOnCurve indicates that the calculated or given point R for some
// signature was not on the curve.
ErrSigRNotOnCurve
ErrSigRNotOnCurve = ErrorKind("ErrSigRNotOnCurve")

// ErrUnequalRValues indicates that the calculated point R for some
// signature was not the same as the given R value for the signature.
ErrUnequalRValues
ErrUnequalRValues = ErrorKind("ErrUnequalRValues")

// ErrSigTooShort is returned when a signature that should be a Schnorr
// signature is too short.
ErrSigTooShort
ErrSigTooShort = ErrorKind("ErrSigTooShort")

// ErrSigTooLong is returned when a signature that should be a Schnorr
// signature is too long.
ErrSigTooLong
ErrSigTooLong = ErrorKind("ErrSigTooLong")

// ErrSigRTooBig is returned when a signature has r with a value that is
// greater than or equal to the prime of the field underlying the group.
ErrSigRTooBig
ErrSigRTooBig = ErrorKind("ErrSigRTooBig")

// ErrSigSTooBig is returned when a signature has s with a value that is
// greater than or equal to the group order.
ErrSigSTooBig

// numErrorCodes is the maximum error code number used in tests. This entry
// MUST be the last entry in the enum.
numErrorCodes
ErrSigSTooBig = ErrorKind("ErrSigSTooBig")
)

// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrInvalidHashLen: "ErrInvalidHashLen",
ErrPrivateKeyIsZero: "ErrPrivateKeyIsZero",
ErrSchnorrHashValue: "ErrSchnorrHashValue",
ErrPubKeyNotOnCurve: "ErrPubKeyNotOnCurve",
ErrSigRYIsOdd: "ErrSigRYIsOdd",
ErrSigRNotOnCurve: "ErrSigRNotOnCurve",
ErrUnequalRValues: "ErrUnequalRValues",
ErrSigTooShort: "ErrSigTooShort",
ErrSigTooLong: "ErrSigTooLong",
ErrSigRTooBig: "ErrSigRTooBig",
ErrSigSTooBig: "ErrSigSTooBig",
}

// String returns the ErrorCode as a human-readable name.
func (e ErrorCode) String() string {
if s := errorCodeStrings[e]; s != "" {
return s
}
return fmt.Sprintf("Unknown ErrorCode (%d)", int(e))
}

// Error implements the error interface.
func (e ErrorCode) Error() string {
return e.String()
}

// Is implements the interface to work with the standard library's errors.Is.
//
// It returns true in the following cases:
// - The target is an Error and the error codes match
// - The target is an ErrorCode and the error codes match
func (e ErrorCode) Is(target error) bool {
switch target := target.(type) {
case Error:
return e == target.ErrorCode

case ErrorCode:
return e == target
}

return false
// Error satisfies the error interface and prints human-readable errors.
func (e ErrorKind) Error() string {
return string(e)
}

// Error identifies a signature-related error. It has full support for
// errors.Is and errors.As, so the caller can ascertain the specific reason for
// the error by checking the underlying error code.
// Error identifies an error related to a schnorr signature. It has full
// support for errors.Is and errors.As, so the caller can ascertain the
// specific reason for the error by checking the underlying error.
type Error struct {
ErrorCode ErrorCode // Describes the kind of error
Description string // Human readable description of the issue
Err error
Description string
}

// Error satisfies the error interface and prints human-readable errors.
func (e Error) Error() string {
return e.Description
}

// Is implements the interface to work with the standard library's errors.Is.
//
// It returns true in the following cases:
// - The target is an Error and the error codes match
// - The target is an ErrorCode and the error codes match
func (e Error) Is(target error) bool {
switch target := target.(type) {
case Error:
return e.ErrorCode == target.ErrorCode

case ErrorCode:
return target == e.ErrorCode
}

return false
}

// Unwrap returns the underlying wrapped error code.
// Unwrap returns the underlying wrapped error.
func (e Error) Unwrap() error {
return e.ErrorCode
return e.Err
}

// signatureError creates an Error given a set of arguments.
func signatureError(c ErrorCode, desc string) Error {
return Error{ErrorCode: c, Description: desc}
func signatureError(kind ErrorKind, desc string) Error {
return Error{Err: kind, Description: desc}
}
39 changes: 13 additions & 26 deletions dcrec/secp256k1/schnorr/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ import (
"testing"
)

// TestErrorCodeStringer tests the stringized output for the ErrorCode type.
func TestErrorCodeStringer(t *testing.T) {
// TestErrorKindStringer tests the stringized output for the ErrorKind type.
func TestErrorKindStringer(t *testing.T) {
tests := []struct {
in ErrorCode
in ErrorKind
want string
}{
{ErrInvalidHashLen, "ErrInvalidHashLen"},
Expand All @@ -26,17 +26,10 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrSigTooLong, "ErrSigTooLong"},
{ErrSigRTooBig, "ErrSigRTooBig"},
{ErrSigSTooBig, "ErrSigSTooBig"},
{0xffff, "Unknown ErrorCode (65535)"},
}

// Detect additional error codes that don't have the stringer added.
if len(tests)-1 != int(numErrorCodes) {
t.Fatalf("It appears an error code was added without adding an " +
"associated stringer test")
}

for i, test := range tests {
result := test.in.String()
result := test.in.Error()
if result != test.want {
t.Errorf("#%d: got: %s want: %s", i, result, test.want)
continue
Expand Down Expand Up @@ -66,15 +59,15 @@ func TestError(t *testing.T) {
}
}

// TestErrorCodeIsAs ensures both ErrorCode and Error can be identified as being
// a specific error code via errors.Is and unwrapped via errors.As.
// TestErrorKindIsAs ensures both ErrorKind and Error can be identified
// as being a specific error via errors.Is and unwrapped via errors.As.
func TestErrorCodeIsAs(t *testing.T) {
tests := []struct {
name string
err error
target error
wantMatch bool
wantAs ErrorCode
wantAs ErrorKind
}{{
name: "ErrInvalidHashLen == ErrInvalidHashLen",
err: ErrInvalidHashLen,
Expand All @@ -87,12 +80,6 @@ func TestErrorCodeIsAs(t *testing.T) {
target: ErrInvalidHashLen,
wantMatch: true,
wantAs: ErrInvalidHashLen,
}, {
name: "ErrInvalidHashLen == Error.ErrInvalidHashLen",
err: ErrInvalidHashLen,
target: signatureError(ErrInvalidHashLen, ""),
wantMatch: true,
wantAs: ErrInvalidHashLen,
}, {
name: "Error.ErrInvalidHashLen == Error.ErrInvalidHashLen",
err: signatureError(ErrInvalidHashLen, ""),
Expand Down Expand Up @@ -134,15 +121,15 @@ func TestErrorCodeIsAs(t *testing.T) {
continue
}

// Ensure the underlying error code can be unwrapped and is the expected
// code.
var code ErrorCode
// Ensure the underlying error kind can be unwrapped and is the
// expected code.
var code ErrorKind
if !errors.As(test.err, &code) {
t.Errorf("%s: unable to unwrap to error code", test.name)
t.Errorf("%s: unable to unwrap to error", test.name)
continue
}
if code != test.wantAs {
t.Errorf("%s: unexpected unwrapped error code -- got %v, want %v",
if !errors.Is(code, test.wantAs) {
t.Errorf("%s: unexpected unwrapped error -- got %v, want %v",
test.name, code, test.wantAs)
continue
}
Expand Down