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 }