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

     1  // Copyright (c) 2018 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  	"errors"
     9  	"fmt"
    10  	"io"
    11  
    12  	"github.com/btcsuite/btcd/chaincfg/chainhash"
    13  )
    14  
    15  const (
    16  	// CFCheckptInterval is the gap (in number of blocks) between each
    17  	// filter header checkpoint.
    18  	CFCheckptInterval = 1000
    19  
    20  	// maxCFHeadersLen is the max number of filter headers we will attempt
    21  	// to decode.
    22  	maxCFHeadersLen = 100000
    23  )
    24  
    25  // ErrInsaneCFHeaderCount signals that we were asked to decode an
    26  // unreasonable number of cfilter headers.
    27  var ErrInsaneCFHeaderCount = errors.New(
    28  	"refusing to decode unreasonable number of filter headers")
    29  
    30  // MsgCFCheckpt implements the Message interface and represents a bitcoin
    31  // cfcheckpt message.  It is used to deliver committed filter header information
    32  // in response to a getcfcheckpt message (MsgGetCFCheckpt). See MsgGetCFCheckpt
    33  // for details on requesting the headers.
    34  type MsgCFCheckpt struct {
    35  	FilterType    FilterType
    36  	StopHash      chainhash.Hash
    37  	FilterHeaders []*chainhash.Hash
    38  }
    39  
    40  // AddCFHeader adds a new committed filter header to the message.
    41  func (msg *MsgCFCheckpt) AddCFHeader(header *chainhash.Hash) error {
    42  	if len(msg.FilterHeaders) == cap(msg.FilterHeaders) {
    43  		str := fmt.Sprintf("FilterHeaders has insufficient capacity for "+
    44  			"additional header: len = %d", len(msg.FilterHeaders))
    45  		return messageError("MsgCFCheckpt.AddCFHeader", str)
    46  	}
    47  
    48  	msg.FilterHeaders = append(msg.FilterHeaders, header)
    49  	return nil
    50  }
    51  
    52  // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
    53  // This is part of the Message interface implementation.
    54  func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error {
    55  	buf := binarySerializer.Borrow()
    56  	defer binarySerializer.Return(buf)
    57  
    58  	// Read filter type
    59  	if _, err := io.ReadFull(r, buf[:1]); err != nil {
    60  		return err
    61  	}
    62  	msg.FilterType = FilterType(buf[0])
    63  
    64  	// Read stop hash
    65  	if _, err := io.ReadFull(r, msg.StopHash[:]); err != nil {
    66  		return err
    67  	}
    68  
    69  	// Read number of filter headers
    70  	count, err := ReadVarIntBuf(r, pver, buf)
    71  	if err != nil {
    72  		return err
    73  	}
    74  
    75  	// Refuse to decode an insane number of cfheaders.
    76  	if count > maxCFHeadersLen {
    77  		return ErrInsaneCFHeaderCount
    78  	}
    79  
    80  	// Create a contiguous slice of hashes to deserialize into in order to
    81  	// reduce the number of allocations.
    82  	msg.FilterHeaders = make([]*chainhash.Hash, count)
    83  	for i := uint64(0); i < count; i++ {
    84  		var cfh chainhash.Hash
    85  		_, err := io.ReadFull(r, cfh[:])
    86  		if err != nil {
    87  			return err
    88  		}
    89  		msg.FilterHeaders[i] = &cfh
    90  	}
    91  
    92  	return nil
    93  }
    94  
    95  // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
    96  // This is part of the Message interface implementation.
    97  func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error {
    98  	buf := binarySerializer.Borrow()
    99  	defer binarySerializer.Return(buf)
   100  
   101  	// Write filter type
   102  	buf[0] = byte(msg.FilterType)
   103  	if _, err := w.Write(buf[:1]); err != nil {
   104  		return err
   105  	}
   106  
   107  	// Write stop hash
   108  	if _, err := w.Write(msg.StopHash[:]); err != nil {
   109  		return err
   110  	}
   111  
   112  	// Write length of FilterHeaders slice
   113  	count := len(msg.FilterHeaders)
   114  	err := WriteVarIntBuf(w, pver, uint64(count), buf)
   115  	if err != nil {
   116  		return err
   117  	}
   118  
   119  	for _, cfh := range msg.FilterHeaders {
   120  		_, err := w.Write(cfh[:])
   121  		if err != nil {
   122  			return err
   123  		}
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  // Deserialize decodes a filter header from r into the receiver using a format
   130  // that is suitable for long-term storage such as a database. This function
   131  // differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
   132  // protocol as it was sent across the network.  The wire encoding can
   133  // technically differ depending on the protocol version and doesn't even really
   134  // need to match the format of a stored filter header at all. As of the time
   135  // this comment was written, the encoded filter header is the same in both
   136  // instances, but there is a distinct difference and separating the two allows
   137  // the API to be flexible enough to deal with changes.
   138  func (msg *MsgCFCheckpt) Deserialize(r io.Reader) error {
   139  	// At the current time, there is no difference between the wire encoding
   140  	// and the stable long-term storage format.  As a result, make use of
   141  	// BtcDecode.
   142  	return msg.BtcDecode(r, 0, BaseEncoding)
   143  }
   144  
   145  // Command returns the protocol command string for the message.  This is part
   146  // of the Message interface implementation.
   147  func (msg *MsgCFCheckpt) Command() string {
   148  	return CmdCFCheckpt
   149  }
   150  
   151  // MaxPayloadLength returns the maximum length the payload can be for the
   152  // receiver. This is part of the Message interface implementation.
   153  func (msg *MsgCFCheckpt) MaxPayloadLength(pver uint32) uint32 {
   154  	// Message size depends on the blockchain height, so return general limit
   155  	// for all messages.
   156  	return MaxMessagePayload
   157  }
   158  
   159  // NewMsgCFCheckpt returns a new bitcoin cfheaders message that conforms to
   160  // the Message interface. See MsgCFCheckpt for details.
   161  func NewMsgCFCheckpt(filterType FilterType, stopHash *chainhash.Hash,
   162  	headersCount int) *MsgCFCheckpt {
   163  	return &MsgCFCheckpt{
   164  		FilterType:    filterType,
   165  		StopHash:      *stopHash,
   166  		FilterHeaders: make([]*chainhash.Hash, 0, headersCount),
   167  	}
   168  }