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 5package wire 6 7import ( 8 "errors" 9 "fmt" 10 "io" 11 12 "github.com/btcsuite/btcd/chaincfg/chainhash" 13) 14 15const ( 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. 27var 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. 34type 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. 41func (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. 54func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) error { 55 // Read filter type 56 err := readElement(r, &msg.FilterType) 57 if err != nil { 58 return err 59 } 60 61 // Read stop hash 62 err = readElement(r, &msg.StopHash) 63 if err != nil { 64 return err 65 } 66 67 // Read number of filter headers 68 count, err := ReadVarInt(r, pver) 69 if err != nil { 70 return err 71 } 72 73 // Refuse to decode an insane number of cfheaders. 74 if count > maxCFHeadersLen { 75 return ErrInsaneCFHeaderCount 76 } 77 78 // Create a contiguous slice of hashes to deserialize into in order to 79 // reduce the number of allocations. 80 msg.FilterHeaders = make([]*chainhash.Hash, count) 81 for i := uint64(0); i < count; i++ { 82 var cfh chainhash.Hash 83 err := readElement(r, &cfh) 84 if err != nil { 85 return err 86 } 87 msg.FilterHeaders[i] = &cfh 88 } 89 90 return nil 91} 92 93// BtcEncode encodes the receiver to w using the bitcoin protocol encoding. 94// This is part of the Message interface implementation. 95func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) error { 96 // Write filter type 97 err := writeElement(w, msg.FilterType) 98 if err != nil { 99 return err 100 } 101 102 // Write stop hash 103 err = writeElement(w, msg.StopHash) 104 if err != nil { 105 return err 106 } 107 108 // Write length of FilterHeaders slice 109 count := len(msg.FilterHeaders) 110 err = WriteVarInt(w, pver, uint64(count)) 111 if err != nil { 112 return err 113 } 114 115 for _, cfh := range msg.FilterHeaders { 116 err := writeElement(w, cfh) 117 if err != nil { 118 return err 119 } 120 } 121 122 return nil 123} 124 125// Deserialize decodes a filter header from r into the receiver using a format 126// that is suitable for long-term storage such as a database. This function 127// differs from BtcDecode in that BtcDecode decodes from the bitcoin wire 128// protocol as it was sent across the network. The wire encoding can 129// technically differ depending on the protocol version and doesn't even really 130// need to match the format of a stored filter header at all. As of the time 131// this comment was written, the encoded filter header is the same in both 132// instances, but there is a distinct difference and separating the two allows 133// the API to be flexible enough to deal with changes. 134func (msg *MsgCFCheckpt) Deserialize(r io.Reader) error { 135 // At the current time, there is no difference between the wire encoding 136 // and the stable long-term storage format. As a result, make use of 137 // BtcDecode. 138 return msg.BtcDecode(r, 0, BaseEncoding) 139} 140 141// Command returns the protocol command string for the message. This is part 142// of the Message interface implementation. 143func (msg *MsgCFCheckpt) Command() string { 144 return CmdCFCheckpt 145} 146 147// MaxPayloadLength returns the maximum length the payload can be for the 148// receiver. This is part of the Message interface implementation. 149func (msg *MsgCFCheckpt) MaxPayloadLength(pver uint32) uint32 { 150 // Message size depends on the blockchain height, so return general limit 151 // for all messages. 152 return MaxMessagePayload 153} 154 155// NewMsgCFCheckpt returns a new bitcoin cfheaders message that conforms to 156// the Message interface. See MsgCFCheckpt for details. 157func NewMsgCFCheckpt(filterType FilterType, stopHash *chainhash.Hash, 158 headersCount int) *MsgCFCheckpt { 159 return &MsgCFCheckpt{ 160 FilterType: filterType, 161 StopHash: *stopHash, 162 FilterHeaders: make([]*chainhash.Hash, 0, headersCount), 163 } 164} 165