github.com/btcsuite/btcd@v0.24.0/wire/msgversion.go (about) 1 // Copyright (c) 2013-2016 The btcsuite developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package wire 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 "strings" 12 "time" 13 ) 14 15 // MaxUserAgentLen is the maximum allowed length for the user agent field in a 16 // version message (MsgVersion). 17 const MaxUserAgentLen = 256 18 19 // DefaultUserAgent for wire in the stack 20 const DefaultUserAgent = "/btcwire:0.5.0/" 21 22 // MsgVersion implements the Message interface and represents a bitcoin version 23 // message. It is used for a peer to advertise itself as soon as an outbound 24 // connection is made. The remote peer then uses this information along with 25 // its own to negotiate. The remote peer must then respond with a version 26 // message of its own containing the negotiated values followed by a verack 27 // message (MsgVerAck). This exchange must take place before any further 28 // communication is allowed to proceed. 29 type MsgVersion struct { 30 // Version of the protocol the node is using. 31 ProtocolVersion int32 32 33 // Bitfield which identifies the enabled services. 34 Services ServiceFlag 35 36 // Time the message was generated. This is encoded as an int64 on the wire. 37 Timestamp time.Time 38 39 // Address of the remote peer. 40 AddrYou NetAddress 41 42 // Address of the local peer. 43 AddrMe NetAddress 44 45 // Unique value associated with message that is used to detect self 46 // connections. 47 Nonce uint64 48 49 // The user agent that generated messsage. This is a encoded as a varString 50 // on the wire. This has a max length of MaxUserAgentLen. 51 UserAgent string 52 53 // Last block seen by the generator of the version message. 54 LastBlock int32 55 56 // Don't announce transactions to peer. 57 DisableRelayTx bool 58 } 59 60 // HasService returns whether the specified service is supported by the peer 61 // that generated the message. 62 func (msg *MsgVersion) HasService(service ServiceFlag) bool { 63 return msg.Services&service == service 64 } 65 66 // AddService adds service as a supported service by the peer generating the 67 // message. 68 func (msg *MsgVersion) AddService(service ServiceFlag) { 69 msg.Services |= service 70 } 71 72 // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. 73 // The version message is special in that the protocol version hasn't been 74 // negotiated yet. As a result, the pver field is ignored and any fields which 75 // are added in new versions are optional. This also mean that r must be a 76 // *bytes.Buffer so the number of remaining bytes can be ascertained. 77 // 78 // This is part of the Message interface implementation. 79 func (msg *MsgVersion) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error { 80 buf, ok := r.(*bytes.Buffer) 81 if !ok { 82 return fmt.Errorf("MsgVersion.BtcDecode reader is not a " + 83 "*bytes.Buffer") 84 } 85 86 err := readElements(buf, &msg.ProtocolVersion, &msg.Services, 87 (*int64Time)(&msg.Timestamp)) 88 if err != nil { 89 return err 90 } 91 92 err = readNetAddress(buf, pver, &msg.AddrYou, false) 93 if err != nil { 94 return err 95 } 96 97 // Protocol versions >= 106 added a from address, nonce, and user agent 98 // field and they are only considered present if there are bytes 99 // remaining in the message. 100 if buf.Len() > 0 { 101 err = readNetAddress(buf, pver, &msg.AddrMe, false) 102 if err != nil { 103 return err 104 } 105 } 106 if buf.Len() > 0 { 107 err = readElement(buf, &msg.Nonce) 108 if err != nil { 109 return err 110 } 111 } 112 if buf.Len() > 0 { 113 userAgent, err := ReadVarString(buf, pver) 114 if err != nil { 115 return err 116 } 117 err = validateUserAgent(userAgent) 118 if err != nil { 119 return err 120 } 121 msg.UserAgent = userAgent 122 } 123 124 // Protocol versions >= 209 added a last known block field. It is only 125 // considered present if there are bytes remaining in the message. 126 if buf.Len() > 0 { 127 err = readElement(buf, &msg.LastBlock) 128 if err != nil { 129 return err 130 } 131 } 132 133 // There was no relay transactions field before BIP0037Version, but 134 // the default behavior prior to the addition of the field was to always 135 // relay transactions. 136 if buf.Len() > 0 { 137 // It's safe to ignore the error here since the buffer has at 138 // least one byte and that byte will result in a boolean value 139 // regardless of its value. Also, the wire encoding for the 140 // field is true when transactions should be relayed, so reverse 141 // it for the DisableRelayTx field. 142 var relayTx bool 143 readElement(r, &relayTx) 144 msg.DisableRelayTx = !relayTx 145 } 146 147 return nil 148 } 149 150 // BtcEncode encodes the receiver to w using the bitcoin protocol encoding. 151 // This is part of the Message interface implementation. 152 func (msg *MsgVersion) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error { 153 err := validateUserAgent(msg.UserAgent) 154 if err != nil { 155 return err 156 } 157 158 err = writeElements(w, msg.ProtocolVersion, msg.Services, 159 msg.Timestamp.Unix()) 160 if err != nil { 161 return err 162 } 163 164 err = writeNetAddress(w, pver, &msg.AddrYou, false) 165 if err != nil { 166 return err 167 } 168 169 err = writeNetAddress(w, pver, &msg.AddrMe, false) 170 if err != nil { 171 return err 172 } 173 174 err = writeElement(w, msg.Nonce) 175 if err != nil { 176 return err 177 } 178 179 err = WriteVarString(w, pver, msg.UserAgent) 180 if err != nil { 181 return err 182 } 183 184 err = writeElement(w, msg.LastBlock) 185 if err != nil { 186 return err 187 } 188 189 // There was no relay transactions field before BIP0037Version. Also, 190 // the wire encoding for the field is true when transactions should be 191 // relayed, so reverse it from the DisableRelayTx field. 192 if pver >= BIP0037Version { 193 err = writeElement(w, !msg.DisableRelayTx) 194 if err != nil { 195 return err 196 } 197 } 198 return nil 199 } 200 201 // Command returns the protocol command string for the message. This is part 202 // of the Message interface implementation. 203 func (msg *MsgVersion) Command() string { 204 return CmdVersion 205 } 206 207 // MaxPayloadLength returns the maximum length the payload can be for the 208 // receiver. This is part of the Message interface implementation. 209 func (msg *MsgVersion) MaxPayloadLength(pver uint32) uint32 { 210 // XXX: <= 106 different 211 212 // Protocol version 4 bytes + services 8 bytes + timestamp 8 bytes + 213 // remote and local net addresses + nonce 8 bytes + length of user 214 // agent (varInt) + max allowed useragent length + last block 4 bytes + 215 // relay transactions flag 1 byte. 216 return 33 + (maxNetAddressPayload(pver) * 2) + MaxVarIntPayload + 217 MaxUserAgentLen 218 } 219 220 // NewMsgVersion returns a new bitcoin version message that conforms to the 221 // Message interface using the passed parameters and defaults for the remaining 222 // fields. 223 func NewMsgVersion(me *NetAddress, you *NetAddress, nonce uint64, 224 lastBlock int32) *MsgVersion { 225 226 // Limit the timestamp to one second precision since the protocol 227 // doesn't support better. 228 return &MsgVersion{ 229 ProtocolVersion: int32(ProtocolVersion), 230 Services: 0, 231 Timestamp: time.Unix(time.Now().Unix(), 0), 232 AddrYou: *you, 233 AddrMe: *me, 234 Nonce: nonce, 235 UserAgent: DefaultUserAgent, 236 LastBlock: lastBlock, 237 DisableRelayTx: false, 238 } 239 } 240 241 // validateUserAgent checks userAgent length against MaxUserAgentLen 242 func validateUserAgent(userAgent string) error { 243 if len(userAgent) > MaxUserAgentLen { 244 str := fmt.Sprintf("user agent too long [len %v, max %v]", 245 len(userAgent), MaxUserAgentLen) 246 return messageError("MsgVersion", str) 247 } 248 return nil 249 } 250 251 // AddUserAgent adds a user agent to the user agent string for the version 252 // message. The version string is not defined to any strict format, although 253 // it is recommended to use the form "major.minor.revision" e.g. "2.6.41". 254 func (msg *MsgVersion) AddUserAgent(name string, version string, 255 comments ...string) error { 256 257 newUserAgent := fmt.Sprintf("%s:%s", name, version) 258 if len(comments) != 0 { 259 newUserAgent = fmt.Sprintf("%s(%s)", newUserAgent, 260 strings.Join(comments, "; ")) 261 } 262 newUserAgent = fmt.Sprintf("%s%s/", msg.UserAgent, newUserAgent) 263 err := validateUserAgent(newUserAgent) 264 if err != nil { 265 return err 266 } 267 msg.UserAgent = newUserAgent 268 return nil 269 }