decred.org/dcrwallet/v3@v3.1.0/errors/errors.go (about)

     1  // Copyright (c) 2018-2019 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  // API originally inspired by https://commandcenter.blogspot.com/2017/12/error-handling-in-upspin.html.
     6  // Currently, dcrwallet is in the process of converting to the new Go 1.13 error
     7  // wrapping features.
     8  
     9  /*
    10  Package errors provides error creation and matching for all wallet systems.  It
    11  is imported as errors and takes over the roll of the standard library errors
    12  package.
    13  */
    14  package errors
    15  
    16  import (
    17  	"errors"
    18  	"fmt"
    19  	"runtime/debug"
    20  	"strings"
    21  )
    22  
    23  // Separator is inserted between nested errors when formatting as strings.  The
    24  // default separator produces easily readable multiline errors.  Separator may
    25  // be modified at init time to create error strings appropriate for logging
    26  // errors on a single line.
    27  var Separator = ":\n\t"
    28  
    29  // Error describes an error condition raised within the wallet process.  Errors
    30  // may optionally provide details regarding the operation and class of error for
    31  // assistance in debugging and runtime matching of errors.
    32  type Error struct {
    33  	Op   Op
    34  	Kind Kind
    35  	Err  error
    36  
    37  	stack  []byte
    38  	bottom bool
    39  }
    40  
    41  // Op describes the operation, method, or RPC in which an error condition was
    42  // raised.
    43  type Op string
    44  
    45  // Opf returns a formatted Op.
    46  func Opf(format string, a ...interface{}) Op {
    47  	return Op(fmt.Sprintf(format, a...))
    48  }
    49  
    50  // Kind describes the class of error.
    51  type Kind int
    52  
    53  // Error kinds.
    54  const (
    55  	Other               Kind = iota // Unclassified error -- does not appear in error strings
    56  	Bug                             // Error is known to be a result of our bug
    57  	Invalid                         // Invalid operation
    58  	Permission                      // Permission denied
    59  	IO                              // I/O error
    60  	Exist                           // Item already exists
    61  	NotExist                        // Item does not exist
    62  	Encoding                        // Invalid encoding
    63  	Crypto                          // Encryption or decryption error
    64  	Locked                          // Wallet is locked
    65  	Passphrase                      // Invalid passphrase
    66  	Seed                            // Invalid seed
    67  	WatchingOnly                    // Missing private keys
    68  	InsufficientBalance             // Insufficient balance to create transaction (perhaps due to UTXO selection requirements)
    69  	ScriptFailure                   // Transaction scripts do not execute (usually due to missing sigs)
    70  	Policy                          // Transaction rejected by wallet policy
    71  	Consensus                       // Consensus violation
    72  	DoubleSpend                     // Transaction is a double spend
    73  	Protocol                        // Protocol violation
    74  	NoPeers                         // Decred network is unreachable due to lack of peers or dcrd RPC connections
    75  	Deployment                      // Inactive consensus deployment
    76  )
    77  
    78  func (k Kind) String() string {
    79  	switch k {
    80  	case Other:
    81  		return "unclassified error"
    82  	case Bug:
    83  		return "internal wallet error"
    84  	case Invalid:
    85  		return "invalid operation"
    86  	case Permission:
    87  		return "permission denied"
    88  	case IO:
    89  		return "I/O error"
    90  	case Exist:
    91  		return "item already exists"
    92  	case NotExist:
    93  		return "item does not exist"
    94  	case Encoding:
    95  		return "invalid encoding"
    96  	case Crypto:
    97  		return "encryption/decryption error"
    98  	case Locked:
    99  		return "wallet locked"
   100  	case Passphrase:
   101  		return "invalid passphrase"
   102  	case Seed:
   103  		return "invalid seed"
   104  	case WatchingOnly:
   105  		return "watching only wallet"
   106  	case InsufficientBalance:
   107  		return "insufficient balance"
   108  	case ScriptFailure:
   109  		return "transaction script fails to execute"
   110  	case Policy:
   111  		return "policy violation"
   112  	case Consensus:
   113  		return "consensus violation"
   114  	case DoubleSpend:
   115  		return "double spend"
   116  	case Protocol:
   117  		return "protocol violation"
   118  	case NoPeers:
   119  		return "Decred network is unreachable"
   120  	case Deployment:
   121  		return "inactive deployment"
   122  	default:
   123  		return "unknown error kind"
   124  	}
   125  }
   126  
   127  func (k Kind) Error() string {
   128  	return k.String()
   129  }
   130  
   131  // As implements the interface to work with the standard library's errors.As.
   132  // If k is Other, this always returns false and target is not assigned.
   133  // If target points to an *Error (i.e. target has type **Error), target is
   134  // assigned an *Error using k as its Kind.
   135  // If target points to a Kind, target is assigned the kind and As returns true.
   136  // Else, target is not assinged and As returns false.
   137  func (k Kind) As(target interface{}) bool {
   138  	if k == Other {
   139  		return false
   140  	}
   141  	switch target := target.(type) {
   142  	case **Error:
   143  		*target = &Error{Kind: k}
   144  		return true
   145  	case *Kind:
   146  		*target = k
   147  		return true
   148  	}
   149  	return false
   150  }
   151  
   152  // New creates a simple error from a string.  New is identical to "errors".New
   153  // from the standard library.
   154  func New(text string) error {
   155  	return errors.New(text)
   156  }
   157  
   158  // Errorf wraps fmt.Errorf as a convenience for creating formatted error
   159  // strings.
   160  func Errorf(format string, args ...interface{}) error {
   161  	return fmt.Errorf(format, args...)
   162  }
   163  
   164  // E creates an *Error from one or more arguments.
   165  //
   166  // Each argument type is inspected when constructing the error.  If multiple
   167  // args of similar type are passed, the final arg is recorded.  The following
   168  // types are recognized:
   169  //
   170  //	errors.Op
   171  //	    The operation, method, or RPC which was invoked.
   172  //	errors.Kind
   173  //	    The class of error.
   174  //	string
   175  //	    Description of the error condition.  String types populate the
   176  //	    Err field and overwrite, and are overwritten by, other arguments
   177  //	    which implement the error interface.
   178  //	error
   179  //	    The underlying error.  If the error is an *Error, the Op and Kind
   180  //	    will be promoted to the newly created error if not set to another
   181  //	    value in the args.
   182  //
   183  // If another *Error is passed as an argument and no other arguments differ from
   184  // the wrapped error, instead of wrapping the error, the errors are collapsed
   185  // and fields of the passed *Error are promoted to the returned error.
   186  //
   187  // Panics if no arguments are passed.
   188  func E(args ...interface{}) error {
   189  	if len(args) == 0 {
   190  		panic("errors.E: no args")
   191  	}
   192  
   193  	var e Error
   194  	e.bottom = true
   195  	var prev *Error
   196  	for _, arg := range args {
   197  		switch arg := arg.(type) {
   198  		case Op:
   199  			e.Op = arg
   200  		case Kind:
   201  			e.Kind = arg
   202  		case string:
   203  			e.Err = New(arg)
   204  			e.bottom = true
   205  		case *Error:
   206  			prev = arg
   207  			if e.Kind == 0 {
   208  				e.Kind = arg.Kind
   209  			}
   210  			e.Err = arg
   211  			e.bottom = false
   212  		case error:
   213  			e.Err = arg
   214  			e.bottom = false
   215  		}
   216  	}
   217  
   218  	// Promote the Op and Kind of the nested Error to the newly created error,
   219  	// if these fields were not part of the args.  This improves matching
   220  	// capabilities as well as improving the order of these fields in the
   221  	// formatted error.
   222  	if e.Err == prev && prev != nil {
   223  		if e.Op == "" {
   224  			e.Op = prev.Op
   225  		}
   226  		if e.Kind == 0 {
   227  			e.Kind = prev.Kind
   228  		}
   229  
   230  		// Remove the previous error from error chain if it does not have any
   231  		// unique fields.
   232  		if (prev.Op == "" || e.Op == prev.Op) && (prev.Kind == 0 || e.Kind == prev.Kind) {
   233  			e.Err = prev.Err
   234  			e.bottom = prev.bottom
   235  			if e.stack == nil {
   236  				e.stack = prev.stack
   237  			}
   238  		}
   239  	}
   240  
   241  	return &e
   242  }
   243  
   244  // WithStack is identical to E but includes a stacktrace with the error. Stack
   245  // traces do not appear in formatted error strings and are not compared when
   246  // matching errors.  Stack traces are extracted from errors using Stacks.
   247  func WithStack(args ...interface{}) error {
   248  	err := E(args...).(*Error)
   249  	err.stack = debug.Stack()
   250  	return err
   251  }
   252  
   253  func (e *Error) Error() string {
   254  	var b strings.Builder
   255  
   256  	// Record the last added fields to the string to avoid duplication.
   257  	var last Error
   258  
   259  	for {
   260  		pad := false // whether to pad/separate next field
   261  		if e.Op != "" && e.Op != last.Op {
   262  			b.WriteString(string(e.Op))
   263  			pad = true
   264  			last.Op = e.Op
   265  		}
   266  		if e.Kind != 0 && e.Kind != last.Kind {
   267  			if pad {
   268  				b.WriteString(": ")
   269  			}
   270  			b.WriteString(e.Kind.String())
   271  			pad = true
   272  			last.Kind = e.Kind
   273  		}
   274  		if e.Err == nil {
   275  			break
   276  		}
   277  		if err, ok := e.Err.(*Error); ok {
   278  			if pad {
   279  				b.WriteString(Separator)
   280  			}
   281  			e = err
   282  			continue
   283  		}
   284  		if pad {
   285  			b.WriteString(": ")
   286  		}
   287  		b.WriteString(e.Err.Error())
   288  		break
   289  	}
   290  
   291  	s := b.String()
   292  	if s == "" {
   293  		return Other.String()
   294  	}
   295  	return s
   296  }
   297  
   298  // Unwrap returns the underlying wrapped error if it is not nil.
   299  // Otherwise, if the Kind is not Other, Unwrap returns the Kind.
   300  // Else, it returns nil.
   301  func (e *Error) Unwrap() error {
   302  	if e.Err != nil {
   303  		return e.Err
   304  	}
   305  	if e.Kind != Other {
   306  		return e.Kind
   307  	}
   308  	return nil
   309  }
   310  
   311  // As implements the interface to work with the standard library's errors.As.
   312  // If target points to an *Error (i.e. target has type **Error), target is
   313  // assigned e and As returns true.
   314  // If target points to a Kind and e's Kind is not Other, target is assigned
   315  // the kind and As returns true.
   316  // Else, target is not assinged and As returns false.
   317  func (e *Error) As(target interface{}) bool {
   318  	switch target := target.(type) {
   319  	case **Error:
   320  		*target = e
   321  		return true
   322  	case *Kind:
   323  		if e.Kind != Other {
   324  			*target = e.Kind
   325  			return true
   326  		}
   327  	}
   328  	return false
   329  }
   330  
   331  // Is implements the interface to work with the standard library's errors.Is.
   332  // If target is an *Error, Is returns true if every top-level and wrapped
   333  // non-zero fields of target are equal to the same fields of e.
   334  // If target is a Kind, Is returns true if the Kinds match and are nonzero.
   335  // Else, Is returns false.
   336  func (e *Error) Is(target error) bool {
   337  	switch target := target.(type) {
   338  	case *Error:
   339  		return match(target, e)
   340  	case Kind:
   341  		return e.Kind != Other && e.Kind == target
   342  	}
   343  	return false
   344  }
   345  
   346  // Is returns whether err equals or wraps target.
   347  func Is(err, target error) bool {
   348  	return errors.Is(err, target)
   349  }
   350  
   351  // As attempts to assign the error pointed to by target with the first error in
   352  // err's error chain with a compatible type.  Returns true if target is
   353  // assigned.
   354  func As(err error, target interface{}) bool {
   355  	return errors.As(err, target)
   356  }
   357  
   358  func match(err1, err2 error) bool {
   359  	e1, ok := err1.(*Error)
   360  	if !ok {
   361  		return false
   362  	}
   363  	e2, ok := err2.(*Error)
   364  	if !ok {
   365  		return false
   366  	}
   367  
   368  	if e1.Op != "" && e1.Op != e2.Op {
   369  		return false
   370  	}
   371  	if e1.Kind != 0 && e1.Kind != e2.Kind {
   372  		return false
   373  	}
   374  	if e1.Err == nil {
   375  		return true
   376  	}
   377  	if e1.Err == e2.Err {
   378  		return true
   379  	}
   380  	if _, ok := e1.Err.(*Error); ok {
   381  		return match(e1.Err, e2.Err)
   382  	}
   383  	// Although errors do not cross the process boundary, comparing error
   384  	// strings is performed to compare formatted errors which would have
   385  	// different allocations.
   386  	return e1.Err.Error() == e2.Err.Error()
   387  }
   388  
   389  // Cause returns the most deeply-nested error from an error chain.
   390  // Cause never returns nil unless the argument is nil.
   391  func Cause(err error) error {
   392  	for {
   393  		wrapper, ok := err.(interface{ Unwrap() error })
   394  		if !ok {
   395  			return err
   396  		}
   397  		e := wrapper.Unwrap()
   398  		if e == nil {
   399  			return err
   400  		}
   401  		err = e
   402  	}
   403  }
   404  
   405  // Stacks extracts all stacktraces from err, sorted from top-most to bottom-most
   406  // error.
   407  func Stacks(err error) [][]byte {
   408  	var stacks [][]byte
   409  	e, _ := err.(*Error)
   410  	for e != nil {
   411  		if e.stack != nil {
   412  			stacks = append(stacks, e.stack)
   413  		}
   414  		e, _ = e.Err.(*Error)
   415  	}
   416  	return stacks
   417  }