-
Notifications
You must be signed in to change notification settings - Fork 1
/
msgversion.go
234 lines (221 loc) · 7.47 KB
/
msgversion.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
package wire
import (
"bytes"
"fmt"
"io"
"strings"
"time"
)
// MaxUserAgentLen is the maximum allowed length for the user agent field in a version message (MsgVersion).
const MaxUserAgentLen = 256
// DefaultUserAgent for wire in the stack
const DefaultUserAgent = "/btcwire:0.5.0/"
// MsgVersion implements the Message interface and represents a bitcoin version message. It is used for a peer to
// advertise itself as soon as an outbound connection is made. The remote peer then uses this information along with its
// own to negotiate. The remote peer must then respond with a version message of its own containing the negotiated
// values followed by a verack message (MsgVerAck). This exchange must take place before any further communication is
// allowed to proceed.
type MsgVersion struct {
// Version of the protocol the node is using.
ProtocolVersion int32
// Bitfield which identifies the enabled services.
Services ServiceFlag
// Time the message was generated. This is encoded as an int64 on the wire.
Timestamp time.Time
// Address of the remote peer.
AddrYou NetAddress
// Address of the local peer.
AddrMe NetAddress
// Unique value associated with message that is used to detect self connections.
Nonce uint64
// The user agent that generated message. This is a encoded as a varString on the wire. This has a max length of
// MaxUserAgentLen.
UserAgent string
// Last block seen by the generator of the version message.
LastBlock int32
// Don't announce transactions to peer.
DisableRelayTx bool
}
// HasService returns whether the specified service is supported by the peer that generated the message.
func (msg *MsgVersion) HasService(service ServiceFlag) bool {
return msg.Services&service == service
}
// AddService adds service as a supported service by the peer generating the message.
func (msg *MsgVersion) AddService(service ServiceFlag) {
msg.Services |= service
}
// BtcDecode decodes r using the bitcoin protocol encoding into the receiver. The version message is special in that the
// protocol version hasn't been negotiated yet. As a result, the pver field is ignored and any fields which are added in
// new versions are optional. This also mean that r must be a *bytes.Buffer so the number of remaining bytes can be
// ascertained. This is part of the Message interface implementation.
func (msg *MsgVersion) BtcDecode(
r io.Reader,
pver uint32,
enc MessageEncoding,
) (e error) {
buf, ok := r.(*bytes.Buffer)
if !ok {
return fmt.Errorf("MsgVersion.BtcDecode reader is not a *bytes.Buffer")
}
if e = readElements(
buf,
&msg.ProtocolVersion,
&msg.Services,
(*int64Time)(&msg.Timestamp),
); E.Chk(e) {
return
}
e = readNetAddress(buf, pver, &msg.AddrYou, false)
if e != nil {
return
}
// Protocol versions >= 106 added a from address, nonce, and user agent field and they are only considered present
// if there are bytes remaining in the message.
if buf.Len() > 0 {
e = readNetAddress(buf, pver, &msg.AddrMe, false)
if e != nil {
return
}
}
if buf.Len() > 0 {
e = readElement(buf, &msg.Nonce)
if e != nil {
return
}
}
if buf.Len() > 0 {
var userAgent string
if userAgent, e = ReadVarString(buf, pver); E.Chk(e) {
return
}
if e = validateUserAgent(userAgent); E.Chk(e) {
return
}
msg.UserAgent = userAgent
}
// Protocol versions >= 209 added a last known block field. It is only
// considered present if there are bytes remaining in the message.
if buf.Len() > 0 {
if e = readElement(buf, &msg.LastBlock); E.Chk(e) {
return
}
}
// There was no relay transactions field before BIP0037Version, but the default
// behavior prior to the addition of the field was to always relay transactions.
if buf.Len() > 0 {
// It's safe to ignore the error here since the buffer has at least one byte and
// that byte will result in a boolean value regardless of its value. Also, the
// wire encoding for the field is true when transactions should be relayed, so
// reverse it for the DisableRelayTx field.
var relayTx bool
if e = readElement(r, &relayTx); E.Chk(e) {
}
msg.DisableRelayTx = !relayTx
}
return
}
// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. This is part of the Message interface
// implementation.
func (msg *MsgVersion) BtcEncode(
w io.Writer,
pver uint32,
enc MessageEncoding,
) (e error) {
if e = validateUserAgent(msg.UserAgent); E.Chk(e) {
return
}
if e = writeElements(
w, msg.ProtocolVersion, msg.Services,
msg.Timestamp.Unix(),
); E.Chk(e) {
return
}
if e = writeNetAddress(w, pver, &msg.AddrYou, false); E.Chk(e) {
return
}
if e = writeNetAddress(w, pver, &msg.AddrMe, false); E.Chk(e) {
return
}
if e = writeElement(w, msg.Nonce); E.Chk(e) {
return
}
if e = WriteVarString(w, pver, msg.UserAgent); E.Chk(e) {
return
}
if e = writeElement(w, msg.LastBlock); E.Chk(e) {
return
}
// There was no relay transactions field before BIP0037Version. Also, the wire encoding for the field is true when
// transactions should be relayed, so reverse it from the DisableRelayTx field.
if pver >= BIP0037Version {
if e = writeElement(w, !msg.DisableRelayTx); E.Chk(e) {
return
}
}
return
}
// Command returns the protocol command string for the message. This is part of the Message interface implementation.
func (msg *MsgVersion) Command() string {
return CmdVersion
}
// MaxPayloadLength returns the maximum length the payload can be for the receiver. This is part of the Message
// interface implementation.
func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 {
// XXX: <= 106 different
// Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes +
// remote and local net addresses + nonce 8 bytes + length of user
// agent (varInt) + max allowed useragent length + last block 4 bytes +
// relay transactions flag 1 byte.
return 33 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload +
MaxUserAgentLen
}
// NewMsgVersion returns a new bitcoin version message that conforms to the Message interface using the passed
// parameters and defaults for the remaining fields.
func NewMsgVersion(
me *NetAddress, you *NetAddress, nonce uint64,
lastBlock int32,
) *MsgVersion {
// Limit the timestamp to one second precision since the protocol doesn't support better.
return &MsgVersion{
ProtocolVersion: int32(ProtocolVersion),
Services: 0,
Timestamp: time.Unix(time.Now().Unix(), 0),
AddrYou: *you,
AddrMe: *me,
Nonce: nonce,
UserAgent: DefaultUserAgent,
LastBlock: lastBlock,
DisableRelayTx: false,
}
}
// validateUserAgent checks userAgent length against MaxUserAgentLen
func validateUserAgent(userAgent string) (e error) {
if len(userAgent) > MaxUserAgentLen {
str := fmt.Sprintf(
"user agent too long [len %v, max %v]",
len(userAgent), MaxUserAgentLen,
)
return messageError("MsgVersion", str)
}
return nil
}
// AddUserAgent adds a user agent to the user agent string for the version message. The version string is not defined
// to any strict format, although it is recommended to use the form "major.minor.revision" e.g. "2.6.41".
func (msg *MsgVersion) AddUserAgent(
name string, version string,
comments ...string,
) (e error) {
newUserAgent := fmt.Sprintf("%s:%s", name, version)
if len(comments) != 0 {
newUserAgent = fmt.Sprintf(
"%s(%s)", newUserAgent,
strings.Join(comments, "; "),
)
}
newUserAgent = fmt.Sprintf("%s%s/", msg.UserAgent, newUserAgent)
if e = validateUserAgent(newUserAgent); E.Chk(e) {
return
}
msg.UserAgent = newUserAgent
return
}