forked from hyperledger-labs/perun-node
-
Notifications
You must be signed in to change notification settings - Fork 1
/
errors.go
382 lines (345 loc) · 11.9 KB
/
errors.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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
// Copyright (c) 2020 - for information on the respective copyright owner
// see the NOTICE file and/or the repository at
// https://github.com/hyperledger-labs/perun-node
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package perun
import (
"fmt"
"io"
"github.com/pkg/errors"
)
// APIError represents the error that will be returned by the API of perun node.
//
// It implements Cause() and Unwrap() methods that implements the underlying
// error, which can further be unwrapped, inspected.
//
// It also implements a customer Formatter, so that the stack trace of
// underlying error is printed when using "%+v" verb.
type apiError struct {
category ErrorCategory
code ErrorCode
err error
addInfo interface{}
}
// Category returns the error category for this API Error.
func (e apiError) Category() ErrorCategory { return e.category }
// Code returns the error code for this API Error.
func (e apiError) Code() ErrorCode { return e.code }
// Message returns the error message for this API Error.
func (e apiError) Message() string { return e.err.Error() }
// AddInfo returns the additional info for this API Error.
func (e apiError) AddInfo() interface{} {
return e.addInfo
}
// Error implement the error interface for API error.
func (e apiError) Error() string {
return fmt.Sprintf("%s %d:%v", e.Category(), e.Code(), e.Message())
}
func (e apiError) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%s %d:%+v", e.Category(), e.Code(), e.err)
return
}
fallthrough
case 's':
//nolint:errcheck,gosec // Error of ioString need not be checked.
io.WriteString(s, e.Error())
case 'q':
fmt.Fprintf(s, "%q", e.Error())
}
}
func (e apiError) Cause() error { return e.err }
func (e apiError) Unwrap() error { return e.err }
// NewAPIErr returns an APIErr with given parameters.
//
// For most use cases, call the error code specific constructor functions.
// This function is intended for use in places only where an APIErr is to be modified.
// For example, in app packages where the data in additional field is to be modified.
// Copy each field, modify and create a new one using this function.
func NewAPIErr(category ErrorCategory, code ErrorCode, err error, addInfo interface{}) APIError {
return apiError{
category: category,
code: code,
err: err,
addInfo: addInfo,
}
}
// NewAPIErrPeerRequestTimedOut returns an ErrPeerRequestTimedOut API Error
// with the given peer alias and response timeout.
//
// It does not validate the time string and it is in a proper format. Eg: 10s.
func NewAPIErrPeerRequestTimedOut(err error, peerAlias, timeout string) APIError {
message := fmt.Sprintf("timed out waiting for response from %s for %s", peerAlias, timeout)
return NewAPIErr(
ParticipantError,
ErrPeerRequestTimedOut,
errors.WithMessage(err, message),
ErrInfoPeerRequestTimedOut{
PeerAlias: peerAlias,
Timeout: timeout,
},
)
}
// NewAPIErrPeerRejected returns an ErrPeerRejected API Error with the
// given peer alias and reason.
func NewAPIErrPeerRejected(err error, peerAlias, reason string) APIError {
message := fmt.Sprintf("peer %s rejected with reason %s", peerAlias, reason)
return NewAPIErr(
ParticipantError,
ErrPeerRejected,
errors.WithMessage(err, message),
ErrInfoPeerRejected{
PeerAlias: peerAlias,
Reason: reason,
},
)
}
// NewAPIErrPeerNotFunded returns an ErrPeerNotFunded API Error with the
// given peer alias.
func NewAPIErrPeerNotFunded(err error, peerAlias string) APIError {
message := fmt.Sprintf("peer %s did not fund within expected time", peerAlias)
return NewAPIErr(
ParticipantError,
ErrPeerNotFunded,
errors.WithMessage(err, message),
ErrInfoPeerNotFunded{
PeerAlias: peerAlias,
},
)
}
// NewAPIErrUserResponseTimedOut returns an ErrUserResponseTimedOut API Error
// with the given expiry.
func NewAPIErrUserResponseTimedOut(expiry, receivedAt int64) APIError {
message := fmt.Sprintf("response received (at %v) after timeout expired (at %v)", receivedAt, expiry)
return NewAPIErr(
ParticipantError,
ErrUserResponseTimedOut,
errors.New(message),
ErrInfoUserResponseTimedOut{
Expiry: expiry,
ReceivedAt: receivedAt,
},
)
}
// ResourceType is used to enumerate valid resource types in ResourceNotFound
// and ResourceExists errors.
//
// The enumeration of valid constants should be defined in the package using
// the error constructors.
type ResourceType string
// NewAPIErrResourceNotFound returns an ErrResourceNotFound API Error with
// the given resource type and ID.
func NewAPIErrResourceNotFound(resourceType ResourceType, resourceID string) APIError {
message := fmt.Sprintf("cannot find %s with ID: %s", resourceType, resourceID)
return NewAPIErr(
ClientError,
ErrResourceNotFound,
errors.New(message),
ErrInfoResourceNotFound{
Type: string(resourceType),
ID: resourceID,
},
)
}
// NewAPIErrResourceExists returns an ErrResourceExists API Error with
// the given resource type and ID.
func NewAPIErrResourceExists(resourceType ResourceType, resourceID string) APIError {
message := fmt.Sprintf("%s with ID: %s already exists", resourceType, resourceID)
return NewAPIErr(
ClientError,
ErrResourceExists,
errors.New(message),
ErrInfoResourceExists{
Type: string(resourceType),
ID: resourceID,
},
)
}
// ArgumentName type is used enumerate valid argument names for use
// InvalidArgument error.
//
// The enumeration of valid constants should be defined in the package using
// the error constructors.
type ArgumentName string
// NewAPIErrInvalidArgument returns an ErrInvalidArgument API Error with the given
// argument name and value.
func NewAPIErrInvalidArgument(err error, name ArgumentName, value string) APIError {
message := fmt.Sprintf("invalid value for %s: %s", name, value)
return NewAPIErr(
ClientError,
ErrInvalidArgument,
errors.WithMessage(err, message),
ErrInfoInvalidArgument{
Name: string(name),
Value: value,
},
)
}
// NewAPIErrFailedPreCondition returns an ErrFailedPreCondition API Error with
// the given error message.
//
// By default, the additional info for this type of err is nil. In case where
// additional info is to be included (to be documented in the API),
// constructors for APIErrFailedPreCondition specific to that case should be used.
func NewAPIErrFailedPreCondition(err error) APIError {
return newAPIErrFailedPreCondition(err, nil)
}
// NewAPIErrFailedPreConditionUnclosedChs returns an ErrFailedPreCondition
// API Error for the special case where channel close was closed with no-force
// option and, one or more open channels exists.
func NewAPIErrFailedPreConditionUnclosedChs(err error, chs []ChInfo) APIError {
return newAPIErrFailedPreCondition(err,
ErrInfoFailedPreCondUnclosedChs{
ChInfos: chs,
})
}
// NewAPIErrFailedPreCondition returns an ErrFailedPreCondition API Error with the given
// error message.
//
// AddInfo is taken as interface because for this error, the callers can
// provide different type of additional info, as defined in the corresponding
// APIs.
func newAPIErrFailedPreCondition(err error, addInfo interface{}) APIError {
message := "failed pre-condition"
return NewAPIErr(
ClientError,
ErrFailedPreCondition,
errors.WithMessage(err, message),
addInfo,
)
}
// NewAPIErrInvalidConfig returns an ErrInvalidConfig, API Error with the given
// config name and value.
func NewAPIErrInvalidConfig(err error, name, value string) APIError {
message := fmt.Sprintf("invalid value for %s: %s", name, value)
return NewAPIErr(
ClientError,
ErrInvalidConfig,
errors.WithMessage(err, message),
ErrInfoInvalidConfig{
Name: name,
Value: value,
},
)
}
// NewAPIErrInvalidContracts returns an ErrInvalidContracts API Error with the
// given contract error infos.
//
// For this error, stack traces of errors for each contract cannot be
// retreived, as there are more than one.
func NewAPIErrInvalidContracts(contractErrInfos ...ContractErrInfo) APIError {
contractsErrInfosMsg := ""
for _, c := range contractErrInfos {
contractsErrInfosMsg += fmt.Sprintf("(%s at address %s: %v) ", c.Name, c.Address, c.Error)
}
message := "invalid contracts: " + contractsErrInfosMsg
return NewAPIErr(
ClientError,
ErrInvalidContracts,
errors.New(message),
ErrInfoInvalidContracts{
ContractErrInfos: contractErrInfos,
},
)
}
// NewAPIErrTxTimedOut returns an ErrTxTimedOut API Error with the given
// error message.
func NewAPIErrTxTimedOut(err error, txType, txID, txTimeout string) APIError {
message := fmt.Sprintf("timed out waiting for %s tx (ID:%s) to be mined in %s: %v", txType, txID, txTimeout, err)
return NewAPIErr(
ProtocolFatalError,
ErrTxTimedOut,
errors.WithMessage(err, message),
ErrInfoTxTimedOut{
TxType: txType,
TxID: txID,
TxTimeout: txTimeout,
},
)
}
// NewAPIErrChainNotReachable returns an ErrChainNotReachable API Error
// with the given error message.
func NewAPIErrChainNotReachable(err error, chainURL string) APIError {
message := fmt.Sprintf("chain not reachable at URL %s", chainURL)
return NewAPIErr(
ProtocolFatalError,
ErrChainNotReachable,
errors.WithMessage(err, message),
ErrInfoChainNotReachable{
ChainURL: chainURL,
},
)
}
// NewAPIErrUnknownInternal returns an ErrUnknownInternal API Error with the given
// error message.
func NewAPIErrUnknownInternal(err error) APIError {
message := "unknown internal error"
return NewAPIErr(
InternalError,
ErrUnknownInternal,
errors.WithMessage(err, message),
nil,
)
}
// APIErrAsMap returns a map containing entries for the method and each of
// the fields in the api error (except message). The map can be directly passed
// to the logger for logging the data in a structured format.
func APIErrAsMap(method string, err APIError) map[string]interface{} {
return map[string]interface{}{
"method": method,
"category": err.Category().String(),
"code": err.Code(),
}
}
// Error type is used to define error constants for this package.
type Error string
// Error implements error interface.
func (e Error) Error() string {
return string(e)
}
// Definition of error constants for this package.
const (
// For failed pre-condition.
ErrChClosed Error = "action not allowed on a closed channel"
ErrSessionClosed Error = "action not allowed on a closed session"
// For invalid config.
ErrUnsupportedType Error = "type not supported, see node config for supported types"
ErrRepeatedPeerAlias Error = "found repeated entries but each value should be unique"
ErrEntryForSelfNotFound Error = "own peer alias (self) not found"
)
// Enumeration of valid resource types for used in ResourceNotFound and
// ResourceExists errors.
const (
ResTypeUpdate ResourceType = "update"
ResTypeUpdateSub ResourceType = "updatesSub"
ResTypeChannel ResourceType = "channel"
ResTypeProposal ResourceType = "proposal"
ResTypeProposalSub ResourceType = "proposalsSub"
ResTypePeerID ResourceType = "peerID"
ResTypeSession ResourceType = "session"
ResTypeCurrency ResourceType = "currency"
)
// Enumeration of valid argument names for using in InvalidArgument error.
const (
ArgNameAmount ArgumentName = "amount"
ArgNameCurrency ArgumentName = "currency"
ArgNamePeerAlias ArgumentName = "peerAlias"
ArgNameOffChainAddr ArgumentName = "offChainAddress"
ArgNameConfigFile ArgumentName = "configFile"
ArgNamePayee ArgumentName = "payee"
ArgNameToken ArgumentName = "token"
ArgNameAsset ArgumentName = "asset"
)