btcd/wire/netaddress.go

168 lines
4.8 KiB
Go
Raw Normal View History

// Copyright (c) 2013-2015 The btcsuite developers
2013-05-08 21:31:00 +02:00
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package wire
2013-05-08 21:31:00 +02:00
import (
"encoding/binary"
"errors"
"io"
"net"
"time"
)
// ErrInvalidNetAddr describes an error that indicates the caller didn't specify
// a TCP address as required.
var ErrInvalidNetAddr = errors.New("provided net.Addr is not a net.TCPAddr")
// maxNetAddressPayload returns the max payload size for a bitcoin NetAddress
// based on the protocol version.
func maxNetAddressPayload(pver uint32) uint32 {
// Services 8 bytes + ip 16 bytes + port 2 bytes.
plen := uint32(26)
// NetAddressTimeVersion added a timestamp field.
if pver >= NetAddressTimeVersion {
// Timestamp 4 bytes.
plen += 4
}
return plen
}
// NetAddress defines information about a peer on the network including the time
// it was last seen, the services it supports, its IP address, and port.
type NetAddress struct {
// Last time the address was seen. This is, unfortunately, encoded as a
// uint32 on the wire and therefore is limited to 2106. This field is
// not present in the bitcoin version message (MsgVersion) nor was it
// added until protocol version >= NetAddressTimeVersion.
Timestamp time.Time
// Bitfield which identifies the services supported by the address.
Services ServiceFlag
// IP address of the peer.
IP net.IP
// Port the peer is using. This is encoded in big endian on the wire
// which differs from most everything else.
Port uint16
}
// HasService returns whether the specified service is supported by the address.
func (na *NetAddress) HasService(service ServiceFlag) bool {
if na.Services&service == service {
return true
}
return false
}
// AddService adds service as a supported service by the peer generating the
// message.
func (na *NetAddress) AddService(service ServiceFlag) {
na.Services |= service
}
// SetAddress is a convenience function to set the IP address and port in one
// call.
func (na *NetAddress) SetAddress(ip net.IP, port uint16) {
na.IP = ip
na.Port = port
}
// NewNetAddressIPPort returns a new NetAddress using the provided IP, port, and
// supported services with defaults for the remaining fields.
func NewNetAddressIPPort(ip net.IP, port uint16, services ServiceFlag) *NetAddress {
// Limit the timestamp to one second precision since the protocol
// doesn't support better.
na := NetAddress{
Timestamp: time.Unix(time.Now().Unix(), 0),
Services: services,
IP: ip,
Port: port,
}
return &na
}
2013-05-08 21:31:00 +02:00
// NewNetAddress returns a new NetAddress using the provided TCP address and
// supported services with defaults for the remaining fields.
//
// Note that addr must be a net.TCPAddr. An ErrInvalidNetAddr is returned
// if it is not.
func NewNetAddress(addr net.Addr, services ServiceFlag) (*NetAddress, error) {
tcpAddr, ok := addr.(*net.TCPAddr)
if !ok {
return nil, ErrInvalidNetAddr
}
na := NewNetAddressIPPort(tcpAddr.IP, uint16(tcpAddr.Port), services)
return na, nil
2013-05-08 21:31:00 +02:00
}
// readNetAddress reads an encoded NetAddress from r depending on the protocol
// version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp.
func readNetAddress(r io.Reader, pver uint32, na *NetAddress, ts bool) error {
var services ServiceFlag
var ip [16]byte
// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
// stop working somewhere around 2106. Also timestamp wasn't added until
// protocol version >= NetAddressTimeVersion
if ts && pver >= NetAddressTimeVersion {
err := readElement(r, (*uint32Time)(&na.Timestamp))
2013-05-08 21:31:00 +02:00
if err != nil {
return err
}
}
err := readElements(r, &services, &ip)
if err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
wire: Reduce allocs with a binary free list. This introduces a new binary free list which provides a concurrent safe list of unused buffers for the purpose of serializing and deserializing primitive integers to their raw binary bytes. For convenience, the type also provides functions for each of the primitive unsigned integers that automatically obtain a buffer from the free list, perform the necessary binary conversion, read from or write to the given io.Reader or io.Writer, and return the buffer to the free list. A global instance of the type has been introduced with a maximum number of 1024 items. Since each buffer is 8 bytes, it will consume a maximum of 8KB. Theoretically, this value would only allow up to 1024 peers simultaneously reading and writing without having to resort to burdening the garbage collector with additional allocations. However, due to the fact the code is designed in such a way that the buffers are quickly used and returned to the free list, in practice it can support much more than 1024 peers without involving the garbage collector since it is highly unlikely every peer would need a buffer at the exact same time. The following is a before and after comparison of the allocations with the benchmarks that did not change removed: benchmark old allocs new allocs delta ------------------------------------------------------------- WriteVarInt1 1 0 -100.00% WriteVarInt3 1 0 -100.00% WriteVarInt5 1 0 -100.00% WriteVarInt9 1 0 -100.00% ReadVarInt1 1 0 -100.00% ReadVarInt3 1 0 -100.00% ReadVarInt5 1 0 -100.00% ReadVarInt9 1 0 -100.00% ReadVarStr4 3 2 -33.33% ReadVarStr10 3 2 -33.33% WriteVarStr4 2 1 -50.00% WriteVarStr10 2 1 -50.00% ReadOutPoint 1 0 -100.00% WriteOutPoint 1 0 -100.00% ReadTxOut 3 1 -66.67% WriteTxOut 2 0 -100.00% ReadTxIn 5 2 -60.00% WriteTxIn 3 0 -100.00% DeserializeTxSmall 15 7 -53.33% DeserializeTxLarge 33428 16715 -50.00% SerializeTx 8 0 -100.00% ReadBlockHeader 7 1 -85.71% WriteBlockHeader 10 4 -60.00% DecodeGetHeaders 1004 501 -50.10% DecodeHeaders 18002 4001 -77.77% DecodeGetBlocks 1004 501 -50.10% DecodeAddr 9002 4001 -55.55% DecodeInv 150005 50003 -66.67% DecodeNotFound 150004 50002 -66.67% DecodeMerkleBlock 222 108 -51.35% TxSha 10 2 -80.00%
2016-04-21 06:03:00 +02:00
port, err := binarySerializer.Uint16(r, bigEndian)
2013-05-08 21:31:00 +02:00
if err != nil {
return err
}
na.Services = services
na.SetAddress(net.IP(ip[:]), port)
return nil
}
// writeNetAddress serializes a NetAddress to w depending on the protocol
// version and whether or not the timestamp is included per ts. Some messages
// like version do not include the timestamp.
func writeNetAddress(w io.Writer, pver uint32, na *NetAddress, ts bool) error {
// NOTE: The bitcoin protocol uses a uint32 for the timestamp so it will
// stop working somewhere around 2106. Also timestamp wasn't added until
// until protocol version >= NetAddressTimeVersion.
if ts && pver >= NetAddressTimeVersion {
err := writeElement(w, uint32(na.Timestamp.Unix()))
if err != nil {
return err
}
}
// Ensure to always write 16 bytes even if the ip is nil.
var ip [16]byte
if na.IP != nil {
copy(ip[:], na.IP.To16())
}
err := writeElements(w, na.Services, ip)
if err != nil {
return err
}
// Sigh. Bitcoin protocol mixes little and big endian.
wire: Reduce allocs with a binary free list. This introduces a new binary free list which provides a concurrent safe list of unused buffers for the purpose of serializing and deserializing primitive integers to their raw binary bytes. For convenience, the type also provides functions for each of the primitive unsigned integers that automatically obtain a buffer from the free list, perform the necessary binary conversion, read from or write to the given io.Reader or io.Writer, and return the buffer to the free list. A global instance of the type has been introduced with a maximum number of 1024 items. Since each buffer is 8 bytes, it will consume a maximum of 8KB. Theoretically, this value would only allow up to 1024 peers simultaneously reading and writing without having to resort to burdening the garbage collector with additional allocations. However, due to the fact the code is designed in such a way that the buffers are quickly used and returned to the free list, in practice it can support much more than 1024 peers without involving the garbage collector since it is highly unlikely every peer would need a buffer at the exact same time. The following is a before and after comparison of the allocations with the benchmarks that did not change removed: benchmark old allocs new allocs delta ------------------------------------------------------------- WriteVarInt1 1 0 -100.00% WriteVarInt3 1 0 -100.00% WriteVarInt5 1 0 -100.00% WriteVarInt9 1 0 -100.00% ReadVarInt1 1 0 -100.00% ReadVarInt3 1 0 -100.00% ReadVarInt5 1 0 -100.00% ReadVarInt9 1 0 -100.00% ReadVarStr4 3 2 -33.33% ReadVarStr10 3 2 -33.33% WriteVarStr4 2 1 -50.00% WriteVarStr10 2 1 -50.00% ReadOutPoint 1 0 -100.00% WriteOutPoint 1 0 -100.00% ReadTxOut 3 1 -66.67% WriteTxOut 2 0 -100.00% ReadTxIn 5 2 -60.00% WriteTxIn 3 0 -100.00% DeserializeTxSmall 15 7 -53.33% DeserializeTxLarge 33428 16715 -50.00% SerializeTx 8 0 -100.00% ReadBlockHeader 7 1 -85.71% WriteBlockHeader 10 4 -60.00% DecodeGetHeaders 1004 501 -50.10% DecodeHeaders 18002 4001 -77.77% DecodeGetBlocks 1004 501 -50.10% DecodeAddr 9002 4001 -55.55% DecodeInv 150005 50003 -66.67% DecodeNotFound 150004 50002 -66.67% DecodeMerkleBlock 222 108 -51.35% TxSha 10 2 -80.00%
2016-04-21 06:03:00 +02:00
err = binary.Write(w, bigEndian, na.Port)
2013-05-08 21:31:00 +02:00
if err != nil {
return err
}
return nil
}