c9ee3d9c5e
SFNodeBloom is a new service flag that a node is required to use to indicate that it supports bloom filtering. This includes a protocol version bump to 70011 and a wire version bump to 0.3.0. btcd: The SFNodeBloom flag is set by default. A new configuration option --nopeerbloomfilters has been added to to disable bloom filtering. Any node advertising a version greater than or equal to 70011 that attempts to use bloom filtering will be disconnected if bloom filtering is disabled. This mimics Bitcoin Core commit afb0ccaf9c9e4e8fac7db3564c4e19c9218c6b03
295 lines
8.4 KiB
Go
295 lines
8.4 KiB
Go
// Copyright (c) 2013-2015 The btcsuite developers
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package wire
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// MaxUserAgentLen is the maximum allowed length for the user agent field in a
|
|
// version message (MsgVersion).
|
|
const MaxUserAgentLen = 2000
|
|
|
|
// DefaultUserAgent for wire in the stack
|
|
const DefaultUserAgent = "/btcwire:0.3.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 messsage. 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 {
|
|
if msg.Services&service == service {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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) error {
|
|
buf, ok := r.(*bytes.Buffer)
|
|
if !ok {
|
|
return fmt.Errorf("MsgVersion.BtcDecode reader is not a " +
|
|
"*bytes.Buffer")
|
|
}
|
|
|
|
var sec int64
|
|
err := readElements(buf, &msg.ProtocolVersion, &msg.Services, &sec)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
msg.Timestamp = time.Unix(sec, 0)
|
|
|
|
err = readNetAddress(buf, pver, &msg.AddrYou, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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 {
|
|
err = readNetAddress(buf, pver, &msg.AddrMe, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if buf.Len() > 0 {
|
|
err = readElement(buf, &msg.Nonce)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
if buf.Len() > 0 {
|
|
userAgent, err := readVarString(buf, pver)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = validateUserAgent(userAgent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
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 {
|
|
err = readElement(buf, &msg.LastBlock)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// 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
|
|
readElement(r, &relayTx)
|
|
msg.DisableRelayTx = !relayTx
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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) error {
|
|
err := validateUserAgent(msg.UserAgent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeElements(w, msg.ProtocolVersion, msg.Services,
|
|
msg.Timestamp.Unix())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeNetAddress(w, pver, &msg.AddrYou, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeNetAddress(w, pver, &msg.AddrMe, false)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeElement(w, msg.Nonce)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeVarString(w, pver, msg.UserAgent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = writeElement(w, msg.LastBlock)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// 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 {
|
|
err = writeElement(w, !msg.DisableRelayTx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// 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,
|
|
}
|
|
}
|
|
|
|
// NewMsgVersionFromConn is a convenience function that extracts the remote
|
|
// and local address from conn and returns a new bitcoin version message that
|
|
// conforms to the Message interface. See NewMsgVersion.
|
|
func NewMsgVersionFromConn(conn net.Conn, nonce uint64,
|
|
lastBlock int32) (*MsgVersion, error) {
|
|
|
|
// Don't assume any services until we know otherwise.
|
|
lna, err := NewNetAddress(conn.LocalAddr(), 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Don't assume any services until we know otherwise.
|
|
rna, err := NewNetAddress(conn.RemoteAddr(), 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return NewMsgVersion(lna, rna, nonce, lastBlock), nil
|
|
}
|
|
|
|
// validateUserAgent checks userAgent length against MaxUserAgentLen
|
|
func validateUserAgent(userAgent string) 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) 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)
|
|
err := validateUserAgent(newUserAgent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
msg.UserAgent = newUserAgent
|
|
return nil
|
|
}
|