inv.go raw

   1  package btcwire
   2  
   3  import (
   4  	"fmt"
   5  
   6  	"git.smesh.lol/nostrino/pkg/crypto/chainhash"
   7  )
   8  
   9  // Inventory vector types.
  10  const (
  11  	// InvVectTypeError is the reserved error type.
  12  	InvVectTypeError uint32 = 0
  13  
  14  	// InvVectTypeTx identifies a transaction by txid.
  15  	InvVectTypeTx uint32 = 1
  16  
  17  	// InvVectTypeBlock identifies a block by block hash.
  18  	InvVectTypeBlock uint32 = 2
  19  
  20  	// InvVectTypeFilteredBlock identifies a filtered block.
  21  	InvVectTypeFilteredBlock uint32 = 3
  22  
  23  	// InvVectTypeWitnessTx identifies a transaction by wtxid.
  24  	InvVectTypeWitnessTx uint32 = 0x40000001
  25  )
  26  
  27  // MaxInvPerMsg is the largest number of inventory vectors a single inv,
  28  // getdata, or notfound message may carry.
  29  const MaxInvPerMsg = 50000
  30  
  31  // InvVect is one inventory vector: a type and the hash of the object it
  32  // identifies.
  33  type InvVect struct {
  34  	// Type is one of the InvVectType constants.
  35  	Type uint32
  36  
  37  	// Hash identifies the object. Its interpretation depends on Type.
  38  	Hash chainhash.Hash
  39  }
  40  
  41  // Inv announces objects a peer has available.
  42  type Inv struct {
  43  	// InvList is the announced inventory.
  44  	InvList []InvVect
  45  }
  46  
  47  // EncodePayload returns the wire encoding of the inv message.
  48  func (v *Inv) EncodePayload() ([]byte, error) {
  49  	return encodeInvList("inv", v.InvList)
  50  }
  51  
  52  // DecodePayload replaces v with the inv message decoded from b.
  53  func (v *Inv) DecodePayload(b []byte) error {
  54  	list, err := decodeInvList("inv", b)
  55  	if err != nil {
  56  		return err
  57  	}
  58  	v.InvList = list
  59  	return nil
  60  }
  61  
  62  // GetData requests objects announced by a peer.
  63  type GetData struct {
  64  	// InvList is the requested inventory.
  65  	InvList []InvVect
  66  }
  67  
  68  // EncodePayload returns the wire encoding of the getdata message.
  69  func (g *GetData) EncodePayload() ([]byte, error) {
  70  	return encodeInvList("getdata", g.InvList)
  71  }
  72  
  73  // DecodePayload replaces g with the getdata message decoded from b.
  74  func (g *GetData) DecodePayload(b []byte) error {
  75  	list, err := decodeInvList("getdata", b)
  76  	if err != nil {
  77  		return err
  78  	}
  79  	g.InvList = list
  80  	return nil
  81  }
  82  
  83  // NotFound reports inventory that a peer could not provide.
  84  type NotFound struct {
  85  	// InvList is the unavailable inventory.
  86  	InvList []InvVect
  87  }
  88  
  89  // EncodePayload returns the wire encoding of the notfound message.
  90  func (n *NotFound) EncodePayload() ([]byte, error) {
  91  	return encodeInvList("notfound", n.InvList)
  92  }
  93  
  94  // DecodePayload replaces n with the notfound message decoded from b.
  95  func (n *NotFound) DecodePayload(b []byte) error {
  96  	list, err := decodeInvList("notfound", b)
  97  	if err != nil {
  98  		return err
  99  	}
 100  	n.InvList = list
 101  	return nil
 102  }
 103  
 104  // invVectSize is the encoded size of one inventory vector.
 105  const invVectSize = 4 + chainhash.HashSize
 106  
 107  // encodeInvList appends a compact size count followed by the inventory
 108  // vectors.
 109  func encodeInvList(name string, list []InvVect) ([]byte, error) {
 110  	if len(list) > MaxInvPerMsg {
 111  		return nil, fmt.Errorf(
 112  			"btcwire: %s carries %d inventory vectors, maximum is %d: %w",
 113  			name, len(list), MaxInvPerMsg, ErrTooManyItems,
 114  		)
 115  	}
 116  	var e Encoder
 117  	e.CompactSize(uint64(len(list)))
 118  	for i := range list {
 119  		e.Uint32(list[i].Type)
 120  		e.Hash(list[i].Hash)
 121  	}
 122  	return e.Bytes(), nil
 123  }
 124  
 125  // decodeInvList decodes a compact size count followed by that many inventory
 126  // vectors.
 127  func decodeInvList(name string, b []byte) ([]InvVect, error) {
 128  	d := NewDecoder(b)
 129  	count, err := d.count(MaxInvPerMsg, invVectSize, name+" inventory vector")
 130  	if err != nil {
 131  		return nil, err
 132  	}
 133  	list := make([]InvVect, count)
 134  	for i := range list {
 135  		if list[i].Type, err = d.Uint32(); err != nil {
 136  			return nil, fmt.Errorf("btcwire: decoding %s inventory type %d: %w", name, i, err)
 137  		}
 138  		if list[i].Hash, err = d.Hash(); err != nil {
 139  			return nil, fmt.Errorf("btcwire: decoding %s inventory hash %d: %w", name, i, err)
 140  		}
 141  	}
 142  	return list, nil
 143  }
 144