1 package wire
2 3 import (
4 "fmt"
5 "io"
6 7 "github.com/p9c/p9/pkg/chainhash"
8 )
9 10 const (
11 // MaxCFHeaderPayload is the maximum byte size of a committed filter header.
12 MaxCFHeaderPayload = chainhash.HashSize
13 // MaxCFHeadersPerMsg is the maximum number of committed filter headers that can be in a single bitcoin cfheaders message.
14 MaxCFHeadersPerMsg = 2000
15 )
16 17 // MsgCFHeaders implements the Message interface and represents a bitcoin cfheaders message. It is used to deliver
18 // committed filter header information in response to a getcfheaders message (MsgGetCFHeaders). The maximum number of
19 // committed filter headers per message is currently 2000. See MsgGetCFHeaders for details on requesting the headers.
20 type MsgCFHeaders struct {
21 FilterType FilterType
22 StopHash chainhash.Hash
23 PrevFilterHeader chainhash.Hash
24 FilterHashes []*chainhash.Hash
25 }
26 27 // AddCFHash adds a new filter hash to the message.
28 func (msg *MsgCFHeaders) AddCFHash(hash *chainhash.Hash) (e error) {
29 if len(msg.FilterHashes)+1 > MaxCFHeadersPerMsg {
30 str := fmt.Sprintf(
31 "too many block headers in message [max %v]",
32 MaxBlockHeadersPerMsg,
33 )
34 return messageError("MsgCFHeaders.AddCFHash", str)
35 }
36 msg.FilterHashes = append(msg.FilterHashes, hash)
37 return nil
38 }
39 40 // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. This is part of the Message interface
41 // implementation.
42 func (msg *MsgCFHeaders) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) (e error) {
43 // Read filter type
44 if e = readElement(r, &msg.FilterType); E.Chk(e) {
45 return
46 }
47 // Read stop hash
48 if e = readElement(r, &msg.StopHash); E.Chk(e) {
49 return
50 }
51 // Read prev filter header
52 if e = readElement(r, &msg.PrevFilterHeader); E.Chk(e) {
53 return
54 }
55 // Read number of filter headers
56 var count uint64
57 if count, e = ReadVarInt(r, pver); E.Chk(e) {
58 return
59 }
60 // Limit to max committed filter headers per message.
61 if count > MaxCFHeadersPerMsg {
62 str := fmt.Sprintf(
63 "too many committed filter headers for "+
64 "message [count %v, max %v]", count,
65 MaxBlockHeadersPerMsg,
66 )
67 return messageError("MsgCFHeaders.BtcDecode", str)
68 }
69 // Create a contiguous slice of hashes to deserialize into in order to reduce the number of allocations.
70 msg.FilterHashes = make([]*chainhash.Hash, 0, count)
71 for i := uint64(0); i < count; i++ {
72 var cfh chainhash.Hash
73 if e = readElement(r, &cfh); E.Chk(e) {
74 return
75 }
76 if e = msg.AddCFHash(&cfh); E.Chk(e) {
77 }
78 }
79 return
80 }
81 82 // BtcEncode encodes the receiver to w using the bitcoin protocol encoding. This is part of the Message interface
83 // implementation.
84 func (msg *MsgCFHeaders) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) (e error) {
85 // Write filter type
86 if e = writeElement(w, msg.FilterType); E.Chk(e) {
87 return
88 }
89 // Write stop hash
90 if e = writeElement(w, msg.StopHash); E.Chk(e) {
91 return
92 }
93 // Write prev filter header
94 if e = writeElement(w, msg.PrevFilterHeader); E.Chk(e) {
95 return
96 }
97 // Limit to max committed headers per message.
98 count := len(msg.FilterHashes)
99 if count > MaxCFHeadersPerMsg {
100 str := fmt.Sprintf(
101 "too many committed filter headers for "+
102 "message [count %v, max %v]", count,
103 MaxBlockHeadersPerMsg,
104 )
105 return messageError("MsgCFHeaders.BtcEncode", str)
106 }
107 if e = WriteVarInt(w, pver, uint64(count)); E.Chk(e) {
108 return
109 }
110 for _, cfh := range msg.FilterHashes {
111 if e = writeElement(w, cfh); E.Chk(e) {
112 return
113 }
114 }
115 return
116 }
117 118 // Deserialize decodes a filter header from r into the receiver using a format that is suitable for long-term storage
119 // such as a database. This function differs from BtcDecode in that BtcDecode decodes from the bitcoin wire protocol as
120 // it was sent across the network. The wire encoding can technically differ depending on the protocol version and
121 // doesn't even really need to match the format of a stored filter header at all. As of the time this comment was
122 // written, the encoded filter header is the same in both instances, but there is a distinct difference and separating
123 // the two allows the API to be flexible enough to deal with changes.
124 func (msg *MsgCFHeaders) Deserialize(r io.Reader) (e error) {
125 // At the current time, there is no difference between the wire encoding and the stable long-term storage format. As
126 // a result, make use of BtcDecode.
127 return msg.BtcDecode(r, 0, BaseEncoding)
128 }
129 130 // Command returns the protocol command string for the message. This is part of the Message interface implementation.
131 func (msg *MsgCFHeaders) Command() string {
132 return CmdCFHeaders
133 }
134 135 // MaxPayloadLength returns the maximum length the payload can be for the receiver. This is part of the Message
136 // interface implementation.
137 func (msg *MsgCFHeaders) MaxPayloadLength(pver uint32) uint32 {
138 // Hash size + filter type + num headers (varInt) + (header size * max headers).
139 return 1 + chainhash.HashSize + chainhash.HashSize + MaxVarIntPayload +
140 (MaxCFHeaderPayload * MaxCFHeadersPerMsg)
141 }
142 143 // NewMsgCFHeaders returns a new bitcoin cfheaders message that conforms to the Message interface. See MsgCFHeaders for
144 // details.
145 func NewMsgCFHeaders() *MsgCFHeaders {
146 return &MsgCFHeaders{
147 FilterHashes: make([]*chainhash.Hash, 0, MaxCFHeadersPerMsg),
148 }
149 }
150