-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
abci.go
166 lines (143 loc) · 4.27 KB
/
abci.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
package errors
import (
"fmt"
"reflect"
abci "github.com/tendermint/tendermint/abci/types"
)
const (
// SuccessABCICode declares an ABCI response use 0 to signal that the
// processing was successful and no error is returned.
SuccessABCICode = 0
// All unclassified errors that do not provide an ABCI code are clubbed
// under an internal error code and a generic message instead of
// detailed error string.
internalABCICodespace = UndefinedCodespace
internalABCICode uint32 = 1
)
// ABCIInfo returns the ABCI error information as consumed by the tendermint
// client. Returned codespace, code, and log message should be used as a ABCI response.
// Any error that does not provide ABCICode information is categorized as error
// with code 1, codespace UndefinedCodespace
// When not running in a debug mode all messages of errors that do not provide
// ABCICode information are replaced with generic "internal error". Errors
// without an ABCICode information as considered internal.
func ABCIInfo(err error, debug bool) (codespace string, code uint32, log string) {
if errIsNil(err) {
return "", SuccessABCICode, ""
}
encode := defaultErrEncoder
if debug {
encode = debugErrEncoder
}
return abciCodespace(err), abciCode(err), encode(err)
}
// ResponseCheckTx returns an ABCI ResponseCheckTx object with fields filled in
// from the given error and gas values.
func ResponseCheckTx(err error, gw, gu uint64, debug bool) abci.ResponseCheckTx {
space, code, log := ABCIInfo(err, debug)
return abci.ResponseCheckTx{
Codespace: space,
Code: code,
Log: log,
GasWanted: int64(gw),
GasUsed: int64(gu),
}
}
// ResponseDeliverTx returns an ABCI ResponseDeliverTx object with fields filled in
// from the given error and gas values.
func ResponseDeliverTx(err error, gw, gu uint64, debug bool) abci.ResponseDeliverTx {
space, code, log := ABCIInfo(err, debug)
return abci.ResponseDeliverTx{
Codespace: space,
Code: code,
Log: log,
GasWanted: int64(gw),
GasUsed: int64(gu),
}
}
// QueryResult returns a ResponseQuery from an error. It will try to parse ABCI
// info from the error.
func QueryResult(err error) abci.ResponseQuery {
space, code, log := ABCIInfo(err, false)
return abci.ResponseQuery{
Codespace: space,
Code: code,
Log: log,
}
}
// The debugErrEncoder encodes the error with a stacktrace.
func debugErrEncoder(err error) string {
return fmt.Sprintf("%+v", err)
}
// The defaultErrEncoder applies Redact on the error before encoding it with its internal error message.
func defaultErrEncoder(err error) string {
return Redact(err).Error()
}
type coder interface {
ABCICode() uint32
}
// abciCode test if given error contains an ABCI code and returns the value of
// it if available. This function is testing for the causer interface as well
// and unwraps the error.
func abciCode(err error) uint32 {
if errIsNil(err) {
return SuccessABCICode
}
for {
if c, ok := err.(coder); ok {
return c.ABCICode()
}
if c, ok := err.(causer); ok {
err = c.Cause()
} else {
return internalABCICode
}
}
}
type codespacer interface {
Codespace() string
}
// abciCodespace tests if given error contains a codespace and returns the value of
// it if available. This function is testing for the causer interface as well
// and unwraps the error.
func abciCodespace(err error) string {
if errIsNil(err) {
return ""
}
for {
if c, ok := err.(codespacer); ok {
return c.Codespace()
}
if c, ok := err.(causer); ok {
err = c.Cause()
} else {
return internalABCICodespace
}
}
}
// errIsNil returns true if value represented by the given error is nil.
//
// Most of the time a simple == check is enough. There is a very narrowed
// spectrum of cases (mostly in tests) where a more sophisticated check is
// required.
func errIsNil(err error) bool {
if err == nil {
return true
}
if val := reflect.ValueOf(err); val.Kind() == reflect.Ptr {
return val.IsNil()
}
return false
}
// Redact replaces an error that is not initialized as an ABCI Error with a
// generic internal error instance. If the error is an ABCI Error, that error is
// simply returned.
func Redact(err error) error {
if ErrPanic.Is(err) {
return ErrPanic
}
if abciCode(err) == internalABCICode {
return errInternal
}
return err
}