From ebe897d3db348349e72fc6e8ef8b73d4b941133f Mon Sep 17 00:00:00 2001 From: Donald Adu-Poku Date: Wed, 16 Dec 2020 23:13:54 +0000 Subject: [PATCH] secp256k1/schnorr: update error types. This updates the Schnorr error types to leverage go 1.13 errors.Is/As functionality as well as conform to the error infrastructure best practices. --- dcrec/secp256k1/schnorr/error.go | 119 ++++++-------------------- dcrec/secp256k1/schnorr/error_test.go | 39 +++------ 2 files changed, 40 insertions(+), 118 deletions(-) diff --git a/dcrec/secp256k1/schnorr/error.go b/dcrec/secp256k1/schnorr/error.go index 2808662808..2b41c07f0f 100644 --- a/dcrec/secp256k1/schnorr/error.go +++ b/dcrec/secp256k1/schnorr/error.go @@ -5,116 +5,68 @@ 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. @@ -122,29 +74,12 @@ 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} } diff --git a/dcrec/secp256k1/schnorr/error_test.go b/dcrec/secp256k1/schnorr/error_test.go index 194a41eaf5..3a75071760 100644 --- a/dcrec/secp256k1/schnorr/error_test.go +++ b/dcrec/secp256k1/schnorr/error_test.go @@ -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"}, @@ -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 @@ -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, @@ -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, ""), @@ -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 }