msgcfcheckpt.go raw

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