Skip to content

Commit

Permalink
dcrec/secp256k1: update error types.
Browse files Browse the repository at this point in the history
This updates the secp256k1 error types to
leverage go 1.13 errors.Is/As functionality as well as confirm to the error infrastructure
best practices.
  • Loading branch information
dnldd authored and davecgh committed Dec 17, 2020
1 parent f20a715 commit f12f386
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 108 deletions.
107 changes: 24 additions & 83 deletions dcrec/secp256k1/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,123 +4,64 @@

package secp256k1

import (
"fmt"
)

// ErrorCode identifies a kind of pubkey-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 (
// ErrPubKeyInvalidLen indicates that the length of a serialized public
// key is not one of the allowed lengths.
ErrPubKeyInvalidLen ErrorCode = iota
ErrPubKeyInvalidLen = ErrorKind("ErrPubKeyInvalidLen")

// ErrPubKeyInvalidFormat indicates an attempt was made to parse a public
// key that does not specify one of the supported formats.
ErrPubKeyInvalidFormat
ErrPubKeyInvalidFormat = ErrorKind("ErrPubKeyInvalidFormat")

// ErrPubKeyXTooBig indicates that the x coordinate for a public key
// is greater than or equal to the prime of the field underlying the group.
ErrPubKeyXTooBig
ErrPubKeyXTooBig = ErrorKind("ErrPubKeyXTooBig")

// ErrPubKeyYTooBig indicates that the y coordinate for a public key is
// greater than or equal to the prime of the field underlying the group.
ErrPubKeyYTooBig
ErrPubKeyYTooBig = ErrorKind("ErrPubKeyYTooBig")

// ErrPubKeyNotOnCurve indicates that a public key is not a point on the
// secp256k1 curve.
ErrPubKeyNotOnCurve
ErrPubKeyNotOnCurve = ErrorKind("ErrPubKeyNotOnCurve")

// ErrPubKeyMismatchedOddness indicates that a hybrid public key specified
// an oddness of the y coordinate that does not match the actual oddness of
// the provided y coordinate.
ErrPubKeyMismatchedOddness

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

// Map of ErrorCode values back to their constant names for pretty printing.
var errorCodeStrings = map[ErrorCode]string{
ErrPubKeyInvalidLen: "ErrPubKeyInvalidLen",
ErrPubKeyXTooBig: "ErrPubKeyXTooBig",
ErrPubKeyYTooBig: "ErrPubKeyYTooBig",
ErrPubKeyNotOnCurve: "ErrPubKeyNotOnCurve",
ErrPubKeyMismatchedOddness: "ErrPubKeyMismatchedOddness",
ErrPubKeyInvalidFormat: "ErrPubKeyInvalidFormat",
}

// 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 a Error and the error codes match
// - The target is a 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 pubkey-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 public key cryptography using a
// sec256k1 curve. 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 a Error and the error codes match
// - The target is a ErrorCode and it 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
}

// makeError creates a Error given a set of arguments.
func makeError(c ErrorCode, desc string) Error {
return Error{ErrorCode: c, Description: desc}
// makeError creates an Error given a set of arguments.
func makeError(kind ErrorKind, desc string) Error {
return Error{Err: kind, Description: desc}
}
37 changes: 12 additions & 25 deletions dcrec/secp256k1/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
}{
{ErrPubKeyInvalidLen, "ErrPubKeyInvalidLen"},
Expand All @@ -21,17 +21,10 @@ func TestErrorCodeStringer(t *testing.T) {
{ErrPubKeyYTooBig, "ErrPubKeyYTooBig"},
{ErrPubKeyNotOnCurve, "ErrPubKeyNotOnCurve"},
{ErrPubKeyMismatchedOddness, "ErrPubKeyMismatchedOddness"},
{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 @@ -61,15 +54,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.
func TestErrorCodeIsAs(t *testing.T) {
// TestErrorKindIsAs ensures both ErrorKind and Error can be identified as being
// a specific error kind via errors.Is and unwrapped via errors.As.
func TestErrorKindIsAs(t *testing.T) {
tests := []struct {
name string
err error
target error
wantMatch bool
wantAs ErrorCode
wantAs ErrorKind
}{{
name: "ErrPubKeyInvalidLen == ErrPubKeyInvalidLen",
err: ErrPubKeyInvalidLen,
Expand All @@ -82,12 +75,6 @@ func TestErrorCodeIsAs(t *testing.T) {
target: ErrPubKeyInvalidLen,
wantMatch: true,
wantAs: ErrPubKeyInvalidLen,
}, {
name: "ErrPubKeyInvalidLen == Error.ErrPubKeyInvalidLen",
err: ErrPubKeyInvalidLen,
target: makeError(ErrPubKeyInvalidLen, ""),
wantMatch: true,
wantAs: ErrPubKeyInvalidLen,
}, {
name: "Error.ErrPubKeyInvalidLen == Error.ErrPubKeyInvalidLen",
err: makeError(ErrPubKeyInvalidLen, ""),
Expand Down Expand Up @@ -131,14 +118,14 @@ func TestErrorCodeIsAs(t *testing.T) {

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

0 comments on commit f12f386

Please sign in to comment.