headers.go raw

   1  package btcwire
   2  
   3  import (
   4  	"fmt"
   5  
   6  	"git.smesh.lol/nostrino/pkg/crypto/chainhash"
   7  )
   8  
   9  // Message vector limits.
  10  const (
  11  	// MaxBlockHeadersPerMsg is the largest number of block headers a headers
  12  	// message may carry.
  13  	MaxBlockHeadersPerMsg = 2000
  14  
  15  	// MaxLocatorHashesPerMsg is the largest number of locator hashes a
  16  	// getheaders message may carry.
  17  	MaxLocatorHashesPerMsg = 101
  18  )
  19  
  20  // GetHeaders requests block headers following the last hash in the locator.
  21  type GetHeaders struct {
  22  	// ProtocolVersion is the sender's protocol version.
  23  	ProtocolVersion uint32
  24  
  25  	// LocatorHashes is the block locator, ordered from the tip back to the
  26  	// genesis block.
  27  	LocatorHashes []chainhash.Hash
  28  
  29  	// StopHash is the hash of the last header to return, or the zero hash to
  30  	// request as many headers as fit in one message.
  31  	StopHash chainhash.Hash
  32  }
  33  
  34  // EncodePayload returns the wire encoding of the getheaders message.
  35  func (g *GetHeaders) EncodePayload() ([]byte, error) {
  36  	if len(g.LocatorHashes) > MaxLocatorHashesPerMsg {
  37  		return nil, fmt.Errorf(
  38  			"btcwire: getheaders carries %d locator hashes, maximum is %d: %w",
  39  			len(g.LocatorHashes), MaxLocatorHashesPerMsg, ErrTooManyItems,
  40  		)
  41  	}
  42  	var e Encoder
  43  	e.Uint32(g.ProtocolVersion)
  44  	e.CompactSize(uint64(len(g.LocatorHashes)))
  45  	for i := range g.LocatorHashes {
  46  		e.Hash(g.LocatorHashes[i])
  47  	}
  48  	e.Hash(g.StopHash)
  49  	return e.Bytes(), nil
  50  }
  51  
  52  // DecodePayload replaces g with the getheaders message decoded from b.
  53  func (g *GetHeaders) DecodePayload(b []byte) error {
  54  	d := NewDecoder(b)
  55  	version, err := d.Uint32()
  56  	if err != nil {
  57  		return fmt.Errorf("btcwire: decoding getheaders protocol version: %w", err)
  58  	}
  59  	count, err := d.count(MaxLocatorHashesPerMsg, chainhash.HashSize, "locator hash")
  60  	if err != nil {
  61  		return err
  62  	}
  63  	hashes := make([]chainhash.Hash, count)
  64  	for i := range hashes {
  65  		if hashes[i], err = d.Hash(); err != nil {
  66  			return fmt.Errorf("btcwire: decoding getheaders locator hash %d: %w", i, err)
  67  		}
  68  	}
  69  	stop, err := d.Hash()
  70  	if err != nil {
  71  		return fmt.Errorf("btcwire: decoding getheaders stop hash: %w", err)
  72  	}
  73  	*g = GetHeaders{
  74  		ProtocolVersion: version,
  75  		LocatorHashes:   hashes,
  76  		StopHash:        stop,
  77  	}
  78  	return nil
  79  }
  80  
  81  // Headers carries a batch of block headers in response to a getheaders
  82  // message.
  83  type Headers struct {
  84  	// Headers is the batch, ordered from the requested height upwards.
  85  	Headers []BlockHeader
  86  }
  87  
  88  // EncodePayload returns the wire encoding of the headers message. Each header
  89  // is followed by a zero transaction count, as required of header-only
  90  // responses.
  91  func (h *Headers) EncodePayload() ([]byte, error) {
  92  	if len(h.Headers) > MaxBlockHeadersPerMsg {
  93  		return nil, fmt.Errorf(
  94  			"btcwire: headers carries %d headers, maximum is %d: %w",
  95  			len(h.Headers), MaxBlockHeadersPerMsg, ErrTooManyItems,
  96  		)
  97  	}
  98  	var e Encoder
  99  	e.CompactSize(uint64(len(h.Headers)))
 100  	for i := range h.Headers {
 101  		raw := h.Headers[i].Serialize()
 102  		e.Write(raw[:])
 103  		e.CompactSize(0)
 104  	}
 105  	return e.Bytes(), nil
 106  }
 107  
 108  // DecodePayload replaces h with the headers message decoded from b. A
 109  // non-zero transaction count after a header is rejected: those entries are not
 110  // valid in a headers message.
 111  func (h *Headers) DecodePayload(b []byte) error {
 112  	d := NewDecoder(b)
 113  	count, err := d.count(
 114  		MaxBlockHeadersPerMsg, BlockHeaderSize+1, "block header",
 115  	)
 116  	if err != nil {
 117  		return err
 118  	}
 119  	headers := make([]BlockHeader, count)
 120  	for i := range headers {
 121  		raw, err := d.Bytes(BlockHeaderSize)
 122  		if err != nil {
 123  			return fmt.Errorf("btcwire: decoding block header %d: %w", i, err)
 124  		}
 125  		if err := headers[i].Deserialize(raw); err != nil {
 126  			return fmt.Errorf("btcwire: decoding block header %d: %w", i, err)
 127  		}
 128  		txCount, err := d.CompactSize()
 129  		if err != nil {
 130  			return fmt.Errorf(
 131  				"btcwire: decoding transaction count of block header %d: %w", i, err,
 132  			)
 133  		}
 134  		if txCount != 0 {
 135  			return fmt.Errorf(
 136  				"%w: headers entry %d has %d transactions",
 137  				ErrInvalidPayload, i, txCount,
 138  			)
 139  		}
 140  	}
 141  	h.Headers = headers
 142  	return nil
 143  }
 144