-
Notifications
You must be signed in to change notification settings - Fork 34
/
main.go
149 lines (128 loc) · 3.4 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
package errors
import (
"fmt"
"github.com/pkg/errors"
abci "github.com/tendermint/tendermint/abci/types"
)
type stackTracer interface {
error
StackTrace() errors.StackTrace
}
type causer interface {
Cause() error
}
// TMError is the tendermint abci return type with stack trace
type TMError interface {
stackTracer
ErrorCode() uint32
Message() string
}
type tmerror struct {
stackTracer
code uint32
msg string
}
var (
_ causer = tmerror{}
_ error = tmerror{}
)
func (t tmerror) ErrorCode() uint32 {
return t.code
}
func (t tmerror) Message() string {
return t.msg
}
func (t tmerror) Cause() error {
if c, ok := t.stackTracer.(causer); ok {
return c.Cause()
}
return t.stackTracer
}
// Format handles "%+v" to expose the full stack trace
// concept from pkg/errors
func (t tmerror) Format(s fmt.State, verb rune) {
// special case also show all info
if verb == 'v' && s.Flag('+') {
fmt.Fprintf(s, "%+v\n", t.stackTracer)
}
// always print the normal error
fmt.Fprintf(s, "(%d) %s\n", t.code, t.msg)
}
// DeliverResult converts any error into a abci.ResponseDeliverTx,
// preserving as much info as possible if it was already
// a TMError
func DeliverResult(err error) abci.ResponseDeliverTx {
tm := Wrap(err)
return abci.ResponseDeliverTx{
Code: tm.ErrorCode(),
Log: tm.Message(),
}
}
// CheckResult converts any error into a abci.ResponseCheckTx,
// preserving as much info as possible if it was already
// a TMError
func CheckResult(err error) abci.ResponseCheckTx {
tm := Wrap(err)
return abci.ResponseCheckTx{
Code: tm.ErrorCode(),
Log: tm.Message(),
}
}
// Wrap safely takes any error and promotes it to a TMError
func Wrap(err error) TMError {
// nil or TMError are no-ops
if err == nil {
return nil
}
// and check for noop
tm, ok := err.(TMError)
if ok {
return tm
}
return WithCode(err, CodeTypeInternalErr)
}
// WithCode adds a stacktrace if necessary and sets the code and msg,
// overriding the state if err was already TMError
func WithCode(err error, code uint32) TMError {
// add a stack only if not present
st, ok := err.(stackTracer)
if !ok {
st = errors.WithStack(err).(stackTracer)
}
// and then wrap it with TMError info
return tmerror{
stackTracer: st,
code: code,
msg: err.Error(),
}
}
// WithMessage prepends some text to the error, then calls WithCode
// It wraps the original error, so IsSameError will still match on err
func WithMessage(prefix string, err error, code uint32) TMError {
e2 := errors.WithMessage(err, prefix)
return WithCode(e2, code)
}
// New adds a stacktrace if necessary and sets the code and msg,
// overriding the state if err was already TMError
func New(msg string, code uint32) TMError {
// create a new error with stack trace and attach a code
st := errors.New(msg).(stackTracer)
return tmerror{
stackTracer: st,
code: code,
msg: msg,
}
}
// IsSameError returns true if these errors have the same root cause.
// pattern is the expected error type and should always be non-nil
// err may be anything and returns true if it is a wrapped version of pattern
func IsSameError(pattern error, err error) bool {
return err != nil && (errors.Cause(err) == errors.Cause(pattern))
}
// HasErrorCode checks if this error would return the named error code
func HasErrorCode(err error, code uint32) bool {
if tm, ok := err.(TMError); ok {
return tm.ErrorCode() == code
}
return code == CodeTypeInternalErr
}