1 package wire
2 3 import (
4 "fmt"
5 "io"
6 7 "github.com/p9c/p9/pkg/chainhash"
8 )
9 10 // RejectCode represents a numeric value by which a remote peer indicates why a message was rejected.
11 type RejectCode uint8
12 13 // These constants define the various supported reject codes.
14 const (
15 RejectMalformed RejectCode = 0x01
16 RejectInvalid RejectCode = 0x10
17 RejectObsolete RejectCode = 0x11
18 RejectDuplicate RejectCode = 0x12
19 RejectNonstandard RejectCode = 0x40
20 RejectDust RejectCode = 0x41
21 RejectInsufficientFee RejectCode = 0x42
22 RejectCheckpoint RejectCode = 0x43
23 )
24 25 // Map of reject codes back strings for pretty printing.
26 var rejectCodeStrings = map[RejectCode]string{
27 RejectMalformed: "REJECT_MALFORMED",
28 RejectInvalid: "REJECT_INVALID",
29 RejectObsolete: "REJECT_OBSOLETE",
30 RejectDuplicate: "REJECT_DUPLICATE",
31 RejectNonstandard: "REJECT_NONSTANDARD",
32 RejectDust: "REJECT_DUST",
33 RejectInsufficientFee: "REJECT_INSUFFICIENTFEE",
34 RejectCheckpoint: "REJECT_CHECKPOINT",
35 }
36 37 // String returns the RejectCode in human-readable form.
38 func (code RejectCode) String() string {
39 if s, ok := rejectCodeStrings[code]; ok {
40 return s
41 }
42 return fmt.Sprintf("Unknown RejectCode (%d)", uint8(code))
43 }
44 45 // MsgReject implements the Message interface and represents a bitcoin reject message. This message was not added until
46 // protocol version RejectVersion.
47 type MsgReject struct {
48 // Cmd is the command for the message which was rejected such as as CmdBlock or CmdTx. This can be obtained from the
49 // Command function of a Message.
50 Cmd string
51 // RejectCode is a code indicating why the command was rejected. It is encoded as a uint8 on the wire.
52 Code RejectCode
53 // Reason is a human-readable string with specific details (over and above the reject code) about why the command
54 // was rejected.
55 Reason string
56 // Hash identifies a specific block or transaction that was rejected and therefore only applies the Block and
57 // MsgTx messages.
58 Hash chainhash.Hash
59 }
60 61 // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. This is part of the Message interface
62 // implementation.
63 func (msg *MsgReject) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) (e error) {
64 if pver < RejectVersion {
65 str := fmt.Sprintf(
66 "reject message invalid for protocol "+
67 "version %d", pver,
68 )
69 return messageError("MsgReject.BtcDecode", str)
70 }
71 // Command that was rejected.
72 var cmd string
73 if cmd, e = ReadVarString(r, pver); E.Chk(e) {
74 return
75 }
76 msg.Cmd = cmd
77 // Code indicating why the command was rejected.
78 e = readElement(r, &msg.Code)
79 if e != nil {
80 return
81 }
82 // Human readable string with specific details (over and above the reject code above) about why the command was
83 // rejected.
84 var reason string
85 if reason, e = ReadVarString(r, pver); E.Chk(e) {
86 return
87 }
88 msg.Reason = reason
89 // CmdBlock and CmdTx messages have an additional hash field that identifies the specific block or transaction.
90 if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
91 if e = readElement(r, &msg.Hash); E.Chk(e) {
92 return
93 }
94 }
95 return
96 }
97 98 // BtcEncode encodes the receiver to w using the bitcoin protocol encoding. This is part of the Message interface
99 // implementation.
100 func (msg *MsgReject) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) (e error) {
101 if pver < RejectVersion {
102 str := fmt.Sprintf(
103 "reject message invalid for protocol "+
104 "version %d", pver,
105 )
106 return messageError("MsgReject.BtcEncode", str)
107 }
108 // Command that was rejected.
109 if e = WriteVarString(w, pver, msg.Cmd); E.Chk(e) {
110 return
111 }
112 // Code indicating why the command was rejected.
113 e = writeElement(w, msg.Code)
114 if e != nil {
115 return
116 }
117 // Human readable string with specific details (over and above the reject code above) about why the command was
118 // rejected.
119 e = WriteVarString(w, pver, msg.Reason)
120 if e != nil {
121 return
122 }
123 // CmdBlock and CmdTx messages have an additional hash field that identifies the specific block or transaction.
124 if msg.Cmd == CmdBlock || msg.Cmd == CmdTx {
125 if e = writeElement(w, &msg.Hash); E.Chk(e) {
126 return
127 }
128 }
129 return nil
130 }
131 132 // Command returns the protocol command string for the message. This is part of the Message interface implementation.
133 func (msg *MsgReject) Command() string {
134 return CmdReject
135 }
136 137 // MaxPayloadLength returns the maximum length the payload can be for the receiver. This is part of the Message
138 // interface implementation.
139 func (msg *MsgReject) MaxPayloadLength(pver uint32) uint32 {
140 plen := uint32(0)
141 // The reject message did not exist before protocol version RejectVersion.
142 if pver >= RejectVersion {
143 // Unfortunately the bitcoin protocol does not enforce a sane limit on the length of the reason, so the max
144 // payload is the overall maximum message payload.
145 plen = MaxMessagePayload
146 }
147 return plen
148 }
149 150 // NewMsgReject returns a new bitcoin reject message that conforms to the Message interface. See MsgReject for details.
151 func NewMsgReject(command string, code RejectCode, reason string) *MsgReject {
152 return &MsgReject{
153 Cmd: command,
154 Code: code,
155 Reason: reason,
156 }
157 }
158