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 }