1 package mempool
2 3 import (
4 "github.com/p9c/p9/pkg/blockchain"
5 "github.com/p9c/p9/pkg/wire"
6 )
7 8 // RuleError identifies a rule violation. It is used to indicate that processing of a transaction failed due to one of
9 // the many validation rules. The caller can use type assertions to determine if a failure was specifically due to a
10 // rule violation and use the Err field to access the underlying error, which will be either a TxRuleError or a
11 // blockchain. RuleError.
12 type RuleError struct {
13 Err error
14 }
15 16 // Error satisfies the error interface and prints human-readable errors.
17 func (e RuleError) Error() string {
18 if e.Err == nil {
19 return "<nil>"
20 }
21 return e.Err.Error()
22 }
23 24 // TxRuleError identifies a rule violation. It is used to indicate that processing of a transaction failed due to one of
25 // the many validation rules. The caller can use type assertions to determine if a failure was specifically due to a
26 // rule violation and access the ErrorCode field to ascertain the specific reason for the rule violation.
27 type TxRuleError struct {
28 RejectCode wire.RejectCode // The code to send with reject messages
29 Description string // Human readable description of the issue
30 }
31 32 // Error satisfies the error interface and prints human-readable errors.
33 func (e TxRuleError) Error() string {
34 return e.Description
35 }
36 37 // txRuleError creates an underlying TxRuleError with the given a set of arguments and returns a RuleError that
38 // encapsulates it.
39 func txRuleError(c wire.RejectCode, desc string) RuleError {
40 return RuleError{
41 Err: TxRuleError{RejectCode: c, Description: desc},
42 }
43 }
44 45 // chainRuleError returns a RuleError that encapsulates the given blockchain. RuleError.
46 func chainRuleError(chainErr blockchain.RuleError) RuleError {
47 return RuleError{
48 Err: chainErr,
49 }
50 }
51 52 // extractRejectCode attempts to return a relevant reject code for a given error by examining the error for known types.
53 // It will return true if a code was successfully extracted.
54 func extractRejectCode(e error) (wire.RejectCode, bool) {
55 // Pull the underlying error out of a RuleError.
56 if rerr, ok := e.(RuleError); ok {
57 e = rerr.Err
58 }
59 switch er := e.(type) {
60 case blockchain.RuleError:
61 // Convert the chain error to a reject code.
62 var code wire.RejectCode
63 switch er.ErrorCode {
64 // Rejected due to duplicate.
65 case blockchain.ErrDuplicateBlock:
66 code = wire.RejectDuplicate
67 // Rejected due to obsolete version.
68 case blockchain.ErrBlockVersionTooOld:
69 code = wire.RejectObsolete
70 // Rejected due to checkpoint.
71 case blockchain.ErrCheckpointTimeTooOld:
72 fallthrough
73 case blockchain.ErrDifficultyTooLow:
74 fallthrough
75 case blockchain.ErrBadCheckpoint:
76 fallthrough
77 case blockchain.ErrForkTooOld:
78 code = wire.RejectCheckpoint
79 // Everything else is due to the block or transaction being invalid.
80 default:
81 code = wire.RejectInvalid
82 }
83 return code, true
84 case TxRuleError:
85 return er.RejectCode, true
86 case nil:
87 return wire.RejectInvalid, false
88 }
89 return wire.RejectInvalid, false
90 }
91 92 // ErrToRejectErr examines the underlying type of the error and returns a reject code and string appropriate to be sent
93 // in a wire.MsgReject message.
94 func ErrToRejectErr(e error) (wire.RejectCode, string) {
95 // Return the reject code along with the error text if it can be
96 // extracted from the error.
97 rejectCode, found := extractRejectCode(e)
98 if found {
99 return rejectCode, e.Error()
100 }
101 // Return a generic rejected string if there is no error. This really should not happen unless the code elsewhere is
102 // not setting an error as it should be but it's best to be safe and simply return a generic string rather than
103 // allowing the following code that dereferences the err to panic.
104 if e == nil {
105 return wire.RejectInvalid, "rejected"
106 }
107 // When the underlying error is not one of the above cases, just return wire.RejectInvalid with a generic rejected
108 // string plus the error text.
109 return wire.RejectInvalid, "rejected: " + e.Error()
110 }
111