wire.go raw

   1  package btcwire
   2  
   3  import (
   4  	"bytes"
   5  	"encoding/binary"
   6  	"errors"
   7  	"fmt"
   8  	"io"
   9  
  10  	"git.smesh.lol/nostrino/pkg/crypto/chainhash"
  11  )
  12  
  13  // Network magic values used to identify the network a frame belongs to.
  14  //
  15  // The values are the conventional four-byte strings in the order they appear
  16  // on the wire, so MainNet is f9 be b4 d9 and RegTest is fa bf b5 da. They are
  17  // therefore written and read big endian: the digits of the constant are the
  18  // bytes on the wire, in the order every Bitcoin document prints them.
  19  const (
  20  	// MainNet is the magic for the Bitcoin main network.
  21  	MainNet uint32 = 0xf9beb4d9
  22  
  23  	// TestNet3 is the magic for the Bitcoin test network, version 3.
  24  	TestNet3 uint32 = 0x0b110907
  25  
  26  	// RegTest is the magic for a Bitcoin regression test network.
  27  	RegTest uint32 = 0xfabfb5da
  28  
  29  	// SigNet is the magic for a Bitcoin signet.
  30  	SigNet uint32 = 0x0a03cf40
  31  )
  32  
  33  // Frame layout constants.
  34  const (
  35  	// CommandSize is the fixed size of the command field in a frame header.
  36  	CommandSize = 12
  37  
  38  	// MessageHeaderSize is the size of a frame header in bytes.
  39  	MessageHeaderSize = 24
  40  
  41  	// ChecksumSize is the number of payload SHA256d bytes carried by a frame
  42  	// header.
  43  	ChecksumSize = 4
  44  
  45  	// MaxPayload is the largest payload this package will encode or accept.
  46  	MaxPayload = 32 << 20
  47  )
  48  
  49  // Command strings used by the messages this package implements. A command is
  50  // at most CommandSize bytes and consists of printable ASCII without NUL.
  51  const (
  52  	CmdVersion      = "version"
  53  	CmdVerAck       = "verack"
  54  	CmdPing         = "ping"
  55  	CmdPong         = "pong"
  56  	CmdGetHeaders   = "getheaders"
  57  	CmdHeaders      = "headers"
  58  	CmdGetCFilters  = "getcfilters"
  59  	CmdCFilter      = "cfilter"
  60  	CmdGetCFHeaders = "getcfheaders"
  61  	CmdCFHeaders    = "cfheaders"
  62  	CmdGetCFCheckpt = "getcfcheckpt"
  63  	CmdCFCheckpt    = "cfcheckpt"
  64  	CmdGetData      = "getdata"
  65  	CmdTx           = "tx"
  66  	CmdBlock        = "block"
  67  	CmdInv          = "inv"
  68  	CmdNotFound     = "notfound"
  69  	CmdSendHeaders  = "sendheaders"
  70  	CmdWtxidRelay   = "wtxidrelay"
  71  	CmdSendAddrV2   = "sendaddrv2"
  72  	CmdFeeFilter    = "feefilter"
  73  	// CmdSendCmpct is how a peer announces compact-block support. A node sends it
  74  	// unprompted right after the handshake even to a client that never asks for BIP152,
  75  	// so a gateway that does not recognise it drops every session.
  76  	CmdSendCmpct = "sendcmpct"
  77  )
  78  
  79  // Message is a decoded frame: a command name and its raw payload.
  80  type Message struct {
  81  	// Command is the command name without NUL padding.
  82  	Command string
  83  
  84  	// Payload is the raw message payload.
  85  	Payload []byte
  86  }
  87  
  88  // Payload is implemented by the message payload types in this package, which
  89  // convert themselves to and from the bytes carried inside a frame.
  90  type Payload interface {
  91  	// EncodePayload returns the wire encoding of the payload.
  92  	EncodePayload() ([]byte, error)
  93  
  94  	// DecodePayload decodes the payload from b.
  95  	DecodePayload(b []byte) error
  96  }
  97  
  98  // Checksum returns the first four bytes of SHA256d(payload), the value carried
  99  // by a frame header.
 100  func Checksum(payload []byte) [ChecksumSize]byte {
 101  	sum := chainhash.DoubleHashB(payload)
 102  	var out [ChecksumSize]byte
 103  	copy(out[:], sum[:ChecksumSize])
 104  	return out
 105  }
 106  
 107  // encodeCommand validates command and returns it NUL padded to CommandSize
 108  // bytes.
 109  func encodeCommand(command string) ([CommandSize]byte, error) {
 110  	var cmd [CommandSize]byte
 111  	switch {
 112  	case len(command) == 0:
 113  		return cmd, fmt.Errorf("%w: empty command", ErrInvalidCommand)
 114  	case len(command) > CommandSize:
 115  		return cmd, fmt.Errorf(
 116  			"%w: %q is %d bytes, maximum is %d",
 117  			ErrInvalidCommand, command, len(command), CommandSize,
 118  		)
 119  	}
 120  	for i := 0; i < len(command); i++ {
 121  		c := command[i]
 122  		if c < 0x20 || c > 0x7e {
 123  			return cmd, fmt.Errorf(
 124  				"%w: %q contains non-printable byte 0x%02x at index %d",
 125  				ErrInvalidCommand, command, c, i,
 126  			)
 127  		}
 128  		cmd[i] = c
 129  	}
 130  	return cmd, nil
 131  }
 132  
 133  // decodeCommand validates a NUL padded command field and returns it without
 134  // the padding.
 135  func decodeCommand(cmd []byte) (string, error) {
 136  	if len(cmd) != CommandSize {
 137  		return "", fmt.Errorf(
 138  			"%w: command field is %d bytes, want %d",
 139  			ErrInvalidCommand, len(cmd), CommandSize,
 140  		)
 141  	}
 142  	n := CommandSize
 143  	for n > 0 && cmd[n-1] == 0 {
 144  		n--
 145  	}
 146  	if n == 0 {
 147  		return "", fmt.Errorf("%w: command is all NUL padding", ErrInvalidCommand)
 148  	}
 149  	for i := 0; i < n; i++ {
 150  		if cmd[i] < 0x20 || cmd[i] > 0x7e {
 151  			return "", fmt.Errorf(
 152  				"%w: command contains non-printable byte 0x%02x at index %d",
 153  				ErrInvalidCommand, cmd[i], i,
 154  			)
 155  		}
 156  	}
 157  	return string(cmd[:n]), nil
 158  }
 159  
 160  // EncodeMessage returns a single complete frame for command and payload.
 161  //
 162  // The frame is the network magic, the NUL padded command, the payload length,
 163  // the payload checksum, and the payload itself. The caller keeps ownership of
 164  // payload; it is copied into the returned frame.
 165  func EncodeMessage(magic uint32, command string, payload []byte) ([]byte, error) {
 166  	cmd, err := encodeCommand(command)
 167  	if err != nil {
 168  		return nil, err
 169  	}
 170  	if len(payload) > MaxPayload {
 171  		return nil, fmt.Errorf(
 172  			"%w: %s payload is %d bytes, maximum is %d",
 173  			ErrPayloadTooLarge, command, len(payload), MaxPayload,
 174  		)
 175  	}
 176  	frame := make([]byte, MessageHeaderSize+len(payload))
 177  	putMagic(frame[0:4], magic)
 178  	copy(frame[4:4+CommandSize], cmd[:])
 179  	binary.LittleEndian.PutUint32(frame[16:20], uint32(len(payload)))
 180  	sum := Checksum(payload)
 181  	copy(frame[20:24], sum[:])
 182  	copy(frame[24:], payload)
 183  	return frame, nil
 184  }
 185  
 186  // EncodeMessagePayload encodes p and returns a frame carrying it under
 187  // command.
 188  func EncodeMessagePayload(magic uint32, command string, p Payload) ([]byte, error) {
 189  	payload, err := p.EncodePayload()
 190  	if err != nil {
 191  		return nil, fmt.Errorf("btcwire: encoding %s payload: %w", command, err)
 192  	}
 193  	return EncodeMessage(magic, command, payload)
 194  }
 195  
 196  // DecodeMessage reads and validates one frame from r. It validates the network
 197  // magic, the command characters, the payload length bound, and the payload
 198  // checksum.
 199  //
 200  // A clean end of input before any header byte is reported as io.EOF; a partial
 201  // header or payload is reported as an error wrapping io.ErrUnexpectedEOF.
 202  func DecodeMessage(magic uint32, r io.Reader) (*Message, error) {
 203  	return decodeMessageLimit(magic, r, MaxPayload)
 204  }
 205  
 206  // decodeMessageLimit is DecodeMessage with a payload cap for this frame.
 207  //
 208  // The cap is applied before the payload is allocated, so a peer that answers a request for an
 209  // 8 MiB block with a 30 MiB frame costs a header read and nothing else.
 210  func decodeMessageLimit(magic uint32, r io.Reader, limit int) (*Message, error) {
 211  	var header [MessageHeaderSize]byte
 212  	if _, err := io.ReadFull(r, header[:]); err != nil {
 213  		return nil, fmt.Errorf("btcwire: reading message header: %w", err)
 214  	}
 215  	gotMagic := readMagic(header[0:4])
 216  	if gotMagic != magic {
 217  		return nil, fmt.Errorf(
 218  			"%w: frame has magic 0x%08x, want 0x%08x",
 219  			ErrBadMagic, gotMagic, magic,
 220  		)
 221  	}
 222  	command, err := decodeCommand(header[4 : 4+CommandSize])
 223  	if err != nil {
 224  		return nil, err
 225  	}
 226  	length := binary.LittleEndian.Uint32(header[16:20])
 227  	if limit <= 0 || limit > MaxPayload {
 228  		limit = MaxPayload
 229  	}
 230  	if length > uint32(limit) {
 231  		return nil, fmt.Errorf(
 232  			"%w: %s payload length %d, maximum is %d",
 233  			ErrPayloadTooLarge, command, length, limit,
 234  		)
 235  	}
 236  	payload := make([]byte, length)
 237  	if _, err := io.ReadFull(r, payload); err != nil {
 238  		// A zero-byte read at end of stream is reported as io.EOF by
 239  		// io.ReadFull, but inside a frame it means truncation, not a clean
 240  		// end of the message stream.
 241  		if errors.Is(err, io.EOF) {
 242  			err = io.ErrUnexpectedEOF
 243  		}
 244  		return nil, fmt.Errorf(
 245  			"btcwire: reading %s payload of %d bytes: %w",
 246  			command, length, err,
 247  		)
 248  	}
 249  	want := Checksum(payload)
 250  	if !bytes.Equal(header[20:24], want[:]) {
 251  		return nil, fmt.Errorf(
 252  			"%w: %s payload of %d bytes has checksum %x, want %x",
 253  			ErrBadChecksum, command, length, header[20:24], want,
 254  		)
 255  	}
 256  	return &Message{Command: command, Payload: payload}, nil
 257  }
 258  
 259  // Decode decodes the message payload into p.
 260  func (m *Message) Decode(p Payload) error {
 261  	if err := p.DecodePayload(m.Payload); err != nil {
 262  		return fmt.Errorf("btcwire: decoding %s payload: %w", m.Command, err)
 263  	}
 264  	return nil
 265  }
 266  
 267  // putMagic writes the four network magic bytes into b, which must be four
 268  // bytes long.
 269  //
 270  // The magic is the one field of the frame header that is big endian. Bitcoin
 271  // prints it as the byte string that goes on the wire ("f9beb4d9" for mainnet),
 272  // so the constant is that byte string read as a big-endian integer and the
 273  // bytes come back out in the order they were printed.
 274  func putMagic(b []byte, magic uint32) {
 275  	binary.BigEndian.PutUint32(b, magic)
 276  }
 277  
 278  // readMagic reads the four network magic bytes in b.
 279  func readMagic(b []byte) uint32 {
 280  	return binary.BigEndian.Uint32(b)
 281  }
 282  
 283  // Reader reads a stream of frames that all belong to the same network.
 284  type Reader struct {
 285  	r     io.Reader
 286  	magic uint32
 287  	limit int
 288  }
 289  
 290  // SetLimit caps the payload of the next frames this reader decodes. Zero or a value above
 291  // MaxPayload restores the package maximum.
 292  func (r *Reader) SetLimit(max int) { r.limit = max }
 293  
 294  // NewReader returns a Reader that reads frames carrying magic from r.
 295  func NewReader(r io.Reader, magic uint32) *Reader {
 296  	return &Reader{r: r, magic: magic}
 297  }
 298  
 299  // Next reads the next message. It returns io.EOF once the underlying reader is
 300  // exhausted at a frame boundary.
 301  func (r *Reader) Next() (*Message, error) {
 302  	limit := r.limit
 303  	if limit <= 0 || limit > MaxPayload {
 304  		limit = MaxPayload
 305  	}
 306  	msg, err := decodeMessageLimit(r.magic, r.r, limit)
 307  	if err != nil {
 308  		if errors.Is(err, io.EOF) {
 309  			return nil, io.EOF
 310  		}
 311  		return nil, err
 312  	}
 313  	return msg, nil
 314  }
 315  
 316  // Magic returns the network magic the Reader validates frames against.
 317  func (r *Reader) Magic() uint32 {
 318  	return r.magic
 319  }
 320