github.com/btcsuite/btcd@v0.24.0/wire/msgcfheaders.go (about)

     1  // Copyright (c) 2017 The btcsuite developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package wire
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  
    11  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    12  )
    13  
    14  const (
    15  	// MaxCFHeaderPayload is the maximum byte size of a committed
    16  	// filter header.
    17  	MaxCFHeaderPayload = chainhash.HashSize
    18  
    19  	// MaxCFHeadersPerMsg is the maximum number of committed filter headers
    20  	// that can be in a single bitcoin cfheaders message.
    21  	MaxCFHeadersPerMsg = 2000
    22  )
    23  
    24  // MsgCFHeaders implements the Message interface and represents a bitcoin
    25  // cfheaders message.  It is used to deliver committed filter header information
    26  // in response to a getcfheaders message (MsgGetCFHeaders). The maximum number
    27  // of committed filter headers per message is currently 2000. See
    28  // MsgGetCFHeaders for details on requesting the headers.
    29  type MsgCFHeaders struct {
    30  	FilterType       FilterType
    31  	StopHash         chainhash.Hash
    32  	PrevFilterHeader chainhash.Hash
    33  	FilterHashes     []*chainhash.Hash
    34  }
    35  
    36  // AddCFHash adds a new filter hash to the message.
    37  func (msg *MsgCFHeaders) AddCFHash(hash *chainhash.Hash) error {
    38  	if len(msg.FilterHashes)+1 > MaxCFHeadersPerMsg {
    39  		str := fmt.Sprintf("too many block headers in message [max %v]",
    40  			MaxBlockHeadersPerMsg)
    41  		return messageError("MsgCFHeaders.AddCFHash", str)
    42  	}
    43  
    44  	msg.FilterHashes = append(msg.FilterHashes, hash)
    45  	return nil
    46  }
    47  
    48  // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
    49  // This is part of the Message interface implementation.
    50  func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
    51  	buf := binarySerializer.Borrow()
    52  	defer binarySerializer.Return(buf)
    53  
    54  	// Read filter type
    55  	if _, err := io.ReadFull(r, buf[:1]); err != nil {
    56  		return err
    57  	}
    58  	msg.FilterType = FilterType(buf[0])
    59  
    60  	// Read stop hash
    61  	if _, err := io.ReadFull(r, msg.StopHash[:]); err != nil {
    62  		return err
    63  	}
    64  
    65  	// Read prev filter header
    66  	if _, err := io.ReadFull(r, msg.PrevFilterHeader[:]); err != nil {
    67  		return err
    68  	}
    69  
    70  	// Read number of filter headers
    71  	count, err := ReadVarIntBuf(r, pver, buf)
    72  	if err != nil {
    73  		return err
    74  	}
    75  
    76  	// Limit to max committed filter headers per message.
    77  	if count > MaxCFHeadersPerMsg {
    78  		str := fmt.Sprintf("too many committed filter headers for "+
    79  			"message [count %v, max %v]", count,
    80  			MaxBlockHeadersPerMsg)
    81  		return messageError("MsgCFHeaders.BtcDecode", str)
    82  	}
    83  
    84  	// Create a contiguous slice of hashes to deserialize into in order to
    85  	// reduce the number of allocations.
    86  	msg.FilterHashes = make([]*chainhash.Hash, 0, count)
    87  	for i := uint64(0); i < count; i++ {
    88  		var cfh chainhash.Hash
    89  		_, err := io.ReadFull(r, cfh[:])
    90  		if err != nil {
    91  			return err
    92  		}
    93  		msg.AddCFHash(&cfh)
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
   100  // This is part of the Message interface implementation.
   101  func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
   102  	count := len(msg.FilterHashes)
   103  	if count > MaxCFHeadersPerMsg {
   104  		str := fmt.Sprintf("too many committed filter headers for "+
   105  			"message [count %v, max %v]", count,
   106  			MaxBlockHeadersPerMsg)
   107  		return messageError("MsgCFHeaders.BtcEncode", str)
   108  	}
   109  
   110  	buf := binarySerializer.Borrow()
   111  	defer binarySerializer.Return(buf)
   112  
   113  	// Write filter type
   114  	buf[0] = byte(msg.FilterType)
   115  	if _, err := w.Write(buf[:1]); err != nil {
   116  		return err
   117  	}
   118  
   119  	// Write stop hash
   120  	if _, err := w.Write(msg.StopHash[:]); err != nil {
   121  		return err
   122  	}
   123  
   124  	// Write prev filter header
   125  	if _, err := w.Write(msg.PrevFilterHeader[:]); err != nil {
   126  		return err
   127  	}
   128  
   129  	err := WriteVarIntBuf(w, pver, uint64(count), buf)
   130  	if err != nil {
   131  		return err
   132  	}
   133  
   134  	for _, cfh := range msg.FilterHashes {
   135  		_, err := w.Write(cfh[:])
   136  		if err != nil {
   137  			return err
   138  		}
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  // Deserialize decodes a filter header from r into the receiver using a format
   145  // that is suitable for long-term storage such as a database. This function
   146  // differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
   147  // protocol as it was sent across the network.  The wire encoding can
   148  // technically differ depending on the protocol version and doesn't even really
   149  // need to match the format of a stored filter header at all. As of the time
   150  // this comment was written, the encoded filter header is the same in both
   151  // instances, but there is a distinct difference and separating the two allows
   152  // the API to be flexible enough to deal with changes.
   153  func (msg *MsgCFHeaders) Deserialize(r io.Reader) error {
   154  	// At the current time, there is no difference between the wire encoding
   155  	// and the stable long-term storage format.  As a result, make use of
   156  	// BtcDecode.
   157  	return msg.BtcDecode(r, 0, BaseEncoding)
   158  }
   159  
   160  // Command returns the protocol command string for the message.  This is part
   161  // of the Message interface implementation.
   162  func (msg *MsgCFHeaders) Command() string {
   163  	return CmdCFHeaders
   164  }
   165  
   166  // MaxPayloadLength returns the maximum length the payload can be for the
   167  // receiver. This is part of the Message interface implementation.
   168  func (msg *MsgCFHeaders) MaxPayloadLength(pver uint32) uint32 {
   169  	// Hash size + filter type + num headers (varInt) +
   170  	// (header size * max headers).
   171  	return 1 + chainhash.HashSize + chainhash.HashSize + MaxVarIntPayload +
   172  		(MaxCFHeaderPayload * MaxCFHeadersPerMsg)
   173  }
   174  
   175  // NewMsgCFHeaders returns a new bitcoin cfheaders message that conforms to
   176  // the Message interface. See MsgCFHeaders for details.
   177  func NewMsgCFHeaders() *MsgCFHeaders {
   178  	return &MsgCFHeaders{
   179  		FilterHashes: make([]*chainhash.Hash, 0, MaxCFHeadersPerMsg),
   180  	}
   181  }