1 package wire
2 3 import (
4 "errors"
5 "fmt"
6 "io"
7 8 "github.com/p9c/p9/pkg/chainhash"
9 )
10 11 const (
12 // CFCheckptInterval is the gap (in number of blocks) between each filter header checkpoint.
13 CFCheckptInterval = 1000
14 // maxCFHeadersLen is the max number of filter headers we will attempt to decode.
15 maxCFHeadersLen = 100000
16 )
17 18 // ErrInsaneCFHeaderCount signals that we were asked to decode an unreasonable number of cfilter headers.
19 var ErrInsaneCFHeaderCount = errors.New(
20 "refusing to decode unreasonable number of filter headers",
21 )
22 23 // MsgCFCheckpt implements the Message interface and represents a bitcoin cfcheckpt message. It is used to deliver
24 // committed filter header information in response to a getcfcheckpt message (MsgGetCFCheckpt). See MsgGetCFCheckpt for
25 // details on requesting the headers.
26 type MsgCFCheckpt struct {
27 FilterType FilterType
28 StopHash chainhash.Hash
29 FilterHeaders []*chainhash.Hash
30 }
31 32 // AddCFHeader adds a new committed filter header to the message.
33 func (msg *MsgCFCheckpt) AddCFHeader(header *chainhash.Hash) (e error) {
34 if len(msg.FilterHeaders) == cap(msg.FilterHeaders) {
35 str := fmt.Sprintf(
36 "FilterHeaders has insufficient capacity for "+
37 "additional header: len = %d", len(msg.FilterHeaders),
38 )
39 return messageError("MsgCFCheckpt.AddCFHeader", str)
40 }
41 msg.FilterHeaders = append(msg.FilterHeaders, header)
42 return nil
43 }
44 45 // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. This is part of the Message interface
46 // implementation.
47 func (msg *MsgCFCheckpt) BtcDecode(r io.Reader, pver uint32, _ MessageEncoding) (e error) {
48 // Read filter type
49 if e = readElement(r, &msg.FilterType); E.Chk(e) {
50 return
51 }
52 // Read stop hash
53 if e = readElement(r, &msg.StopHash); E.Chk(e) {
54 return
55 }
56 // Read number of filter headers
57 var count uint64
58 if count, e = ReadVarInt(r, pver); E.Chk(e) {
59 return
60 }
61 // Refuse to decode an insane number of cfheaders.
62 if count > maxCFHeadersLen {
63 return ErrInsaneCFHeaderCount
64 }
65 // Create a contiguous slice of hashes to deserialize into in order to reduce
66 // the number of allocations.
67 msg.FilterHeaders = make([]*chainhash.Hash, count)
68 for i := uint64(0); i < count; i++ {
69 var cfh chainhash.Hash
70 if e = readElement(r, &cfh); E.Chk(e) {
71 return
72 }
73 msg.FilterHeaders[i] = &cfh
74 }
75 return
76 }
77 78 // BtcEncode encodes the receiver to w using the bitcoin protocol encoding. This
79 // is part of the Message interface implementation.
80 func (msg *MsgCFCheckpt) BtcEncode(w io.Writer, pver uint32, _ MessageEncoding) (e error) {
81 // Write filter type
82 if e = writeElement(w, msg.FilterType); E.Chk(e) {
83 return
84 }
85 // Write stop hash
86 if e = writeElement(w, msg.StopHash); E.Chk(e) {
87 return
88 }
89 // Write length of FilterHeaders slice
90 count := len(msg.FilterHeaders)
91 e = WriteVarInt(w, pver, uint64(count))
92 if E.Chk(e) {
93 return
94 }
95 for _, cfh := range msg.FilterHeaders {
96 if e = writeElement(w, cfh); E.Chk(e) {
97 return
98 }
99 }
100 return
101 }
102 103 // Deserialize decodes a filter header from r into the receiver using a format
104 // that is suitable for long-term storage such as a database. This function
105 // differs from BtcDecode in that BtcDecode decodes from the bitcoin wire
106 // protocol as it was sent across the network. The wire encoding can technically
107 // differ depending on the protocol version and doesn't even really need to
108 // match the format of a stored filter header at all. As of the time this
109 // comment was written, the encoded filter header is the same in both instances,
110 // but there is a distinct difference and separating the two allows the API to
111 // be flexible enough to deal with changes.
112 func (msg *MsgCFCheckpt) Deserialize(r io.Reader) (e error) {
113 // At the current time, there is no difference between the wire encoding and the
114 // stable long-term storage format. As a result, make use of BtcDecode.
115 return msg.BtcDecode(r, 0, BaseEncoding)
116 }
117 118 // Command returns the protocol command string for the message. This is part of the Message interface implementation.
119 func (msg *MsgCFCheckpt) Command() string {
120 return CmdCFCheckpt
121 }
122 123 // MaxPayloadLength returns the maximum length the payload can be for the
124 // receiver. This is part of the Message interface implementation.
125 func (msg *MsgCFCheckpt) MaxPayloadLength(pver uint32) uint32 {
126 // Message size depends on the blockchain height, so return general limit for
127 // all messages.
128 return MaxMessagePayload
129 }
130 131 // NewMsgCFCheckpt returns a new bitcoin cfheaders message that conforms to the
132 // Message interface. See MsgCFCheckpt for details.
133 func NewMsgCFCheckpt(filterType FilterType, stopHash *chainhash.Hash, headersCount int,) *MsgCFCheckpt {
134 return &MsgCFCheckpt{
135 FilterType: filterType,
136 StopHash: *stopHash,
137 FilterHeaders: make([]*chainhash.Hash, 0, headersCount),
138 }
139 }
140