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