1 package wire
2 3 import (
4 "bytes"
5 "fmt"
6 "io"
7 )
8 9 // MsgAlert contains a payload and a signature:
10 //
11 // ===============================================
12 // | Field | Data Type | Size |
13 // ===============================================
14 // | payload | []uchar | ? |
15 // -----------------------------------------------
16 // | signature | []uchar | ? |
17 // -----------------------------------------------
18 //
19 // Here payload is an Alert serialized into a byte array to ensure that
20 // versions using incompatible alert formats can still relay
21 // alerts among one another.
22 //
23 // An Alert is the payload deserialized as follows:
24 //
25 // ===============================================
26 // | Field | Data Type | Size |
27 // ===============================================
28 // | Version | int32 | 4 |
29 // -----------------------------------------------
30 // | RelayUntil | int64 | 8 |
31 // -----------------------------------------------
32 // | Expiration | int64 | 8 |
33 // -----------------------------------------------
34 // | ID | int32 | 4 |
35 // -----------------------------------------------
36 // | Cancel | int32 | 4 |
37 // -----------------------------------------------
38 // | SetCancel | set<int32> | ? |
39 // -----------------------------------------------
40 // | MinVer | int32 | 4 |
41 // -----------------------------------------------
42 // | MaxVer | int32 | 4 |
43 // -----------------------------------------------
44 // | SetSubVer | set<string> | ? |
45 // -----------------------------------------------;
46 // | Priority | int32 | 4 |
47 // -----------------------------------------------
48 // | Comment | string | ? |
49 // -----------------------------------------------
50 // | StatusBar | string | ? |
51 // -----------------------------------------------
52 // | Reserved | string | ? |
53 // -----------------------------------------------
54 // | Total (Fixed) | 45 |
55 // -----------------------------------------------
56 //
57 // NOTE:
58 //
59 // * string is a VarString i.e VarInt length followed by the string itself
60 // * set<string> is a VarInt followed by as many number of strings
61 // * set<int32> is a VarInt followed by as many number of ints
62 // * fixedAlertSize = 40 + 5*min(VarInt) = 40 + 5*1 = 45
63 //
64 // Now we can define bounds on Alert size, SetCancel and SetSubVer Fixed size of the alert payload
65 const fixedAlertSize = 45
66 67 // maxSignatureSize is the max size of an ECDSA signature. NOTE: Since this size is fixed and < 255, the size of VarInt required = 1.
68 const maxSignatureSize = 72
69 70 // maxAlertSize is the maximum size an alert. MessagePayload = VarInt(Alert) + Alert + VarInt(Signature) + Signature
71 // MaxMessagePayload = maxAlertSize + max(VarInt) + maxSignatureSize + 1
72 const maxAlertSize = MaxMessagePayload - maxSignatureSize - MaxVarIntPayload - 1
73 74 // maxCountSetCancel is the maximum number of cancel IDs that could possibly fit into a maximum size alert.
75 // maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string) for caculating maximum number of cancel IDs, set all other var txsizes to 0
76 // maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(int32)
77 // x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
78 const maxCountSetCancel = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
79 80 // maxCountSetSubVer is the maximum number of subversions that could possibly fit into a maximum size alert.
81 // maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
82 // for caculating maximum number of subversions, set all other var txsizes to 0
83 // maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(string)
84 // x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / sizeOf(string)
85 // subversion would typically be something like "/Satoshi:0.7.2/" (15 bytes) so assuming < 255 bytes, sizeOf(string) = sizeOf(uint8) + 255 = 256
86 const maxCountSetSubVer = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 256
87 88 // Alert contains the data deserialized from the MsgAlert payload.
89 type Alert struct {
90 // Alert format version
91 Version int32
92 // Timestamp beyond which nodes should stop relaying this alert
93 RelayUntil int64
94 // Timestamp beyond which this alert is no longer in effect and should be
95 // ignored
96 Expiration int64
97 // A unique ID number for this alert
98 ID int32
99 // All alerts with an ID less than or equal to this number should cancelled,
100 // deleted and not accepted in the future
101 Cancel int32
102 // All alert IDs contained in this set should be cancelled as above
103 SetCancel []int32
104 // This alert only applies to versions greater than or equal to this version.
105 // Other versions should still relay it.
106 MinVer int32
107 // This alert only applies to versions less than or equal to this version. Other
108 // versions should still relay it.
109 MaxVer int32
110 // If this set contains any elements, then only nodes that have their subVer
111 // contained in this set are affected by the alert. Other versions should still
112 // relay it.
113 SetSubVer []string
114 // Relative priority compared to other alerts
115 Priority int32
116 // A comment on the alert that is not displayed
117 Comment string
118 // The alert message that is displayed to the user
119 StatusBar string
120 // Reserved
121 Reserved string
122 }
123 124 // Serialize encodes the alert to w using the alert protocol encoding format.
125 func (alert *Alert) Serialize(w io.Writer, pver uint32) (e error) {
126 if e = writeElements(w, alert.Version, alert.RelayUntil, alert.Expiration, alert.ID, alert.Cancel); E.Chk(e) {
127 return
128 }
129 count := len(alert.SetCancel)
130 if count > maxCountSetCancel {
131 str := fmt.Sprintf(
132 "too many cancel alert IDs for alert [count %v, max %v]", count, maxCountSetCancel,
133 )
134 return messageError("Alert.Serialize", str)
135 }
136 if e = WriteVarInt(w, pver, uint64(count)); E.Chk(e) {
137 return
138 }
139 for i := 0; i < count; i++ {
140 if e = writeElement(w, alert.SetCancel[i]); E.Chk(e) {
141 return
142 }
143 }
144 if e = writeElements(w, alert.MinVer, alert.MaxVer); E.Chk(e) {
145 return
146 }
147 count = len(alert.SetSubVer)
148 if count > maxCountSetSubVer {
149 str := fmt.Sprintf(
150 "too many sub versions for alert [count %v, max %v]", count, maxCountSetSubVer,
151 )
152 return messageError("Alert.Serialize", str)
153 }
154 if e = WriteVarInt(w, pver, uint64(count)); E.Chk(e) {
155 return
156 }
157 for i := 0; i < count; i++ {
158 if e = WriteVarString(w, pver, alert.SetSubVer[i]); E.Chk(e) {
159 return
160 }
161 }
162 if e = writeElement(w, alert.Priority); E.Chk(e) {
163 return
164 }
165 if e = WriteVarString(w, pver, alert.Comment); E.Chk(e) {
166 return
167 }
168 if e = WriteVarString(w, pver, alert.StatusBar); E.Chk(e) {
169 return
170 }
171 return WriteVarString(w, pver, alert.Reserved)
172 }
173 174 // Deserialize decodes from r into the receiver using the alert protocol encoding format.
175 func (alert *Alert) Deserialize(r io.Reader, pver uint32) (e error) {
176 if e = readElements(
177 r, &alert.Version, &alert.RelayUntil,
178 &alert.Expiration, &alert.ID, &alert.Cancel,
179 ); E.Chk(e) {
180 return
181 }
182 // SetCancel: first read a VarInt that contains count - the number of Cancel
183 // IDs, then iterate count times and read them
184 var count uint64
185 if count, e = ReadVarInt(r, pver); E.Chk(e) {
186 return
187 }
188 if count > maxCountSetCancel {
189 str := fmt.Sprintf(
190 "too many cancel alert IDs for alert "+
191 "[count %v, max %v]", count, maxCountSetCancel,
192 )
193 return messageError("Alert.Deserialize", str)
194 }
195 alert.SetCancel = make([]int32, count)
196 for i := 0; i < int(count); i++ {
197 if e = readElement(r, &alert.SetCancel[i]); E.Chk(e) {
198 return
199 }
200 }
201 if e = readElements(r, &alert.MinVer, &alert.MaxVer); E.Chk(e) {
202 return
203 }
204 // SetSubVer: similar to SetCancel but read count number of sub-version strings
205 if count, e = ReadVarInt(r, pver); E.Chk(e) {
206 return
207 }
208 if count > maxCountSetSubVer {
209 str := fmt.Sprintf(
210 "too many sub versions for alert [count %v, max %v]", count, maxCountSetSubVer,
211 )
212 return messageError("Alert.Deserialize", str)
213 }
214 alert.SetSubVer = make([]string, count)
215 for i := 0; i < int(count); i++ {
216 if alert.SetSubVer[i], e = ReadVarString(r, pver); E.Chk(e) {
217 return
218 }
219 }
220 if e = readElement(r, &alert.Priority); E.Chk(e) {
221 return
222 }
223 if alert.Comment, e = ReadVarString(r, pver); E.Chk(e) {
224 return
225 }
226 if alert.StatusBar, e = ReadVarString(r, pver); E.Chk(e) {
227 return
228 }
229 alert.Reserved, e = ReadVarString(r, pver)
230 return
231 }
232 233 // NewAlert returns an new Alert with values provided.
234 func NewAlert(
235 version int32, relayUntil int64, expiration int64,
236 id int32, cancel int32, setCancel []int32, minVer int32,
237 maxVer int32, setSubVer []string, priority int32, comment string,
238 statusBar string,
239 ) *Alert {
240 return &Alert{
241 Version: version,
242 RelayUntil: relayUntil,
243 Expiration: expiration,
244 ID: id,
245 Cancel: cancel,
246 SetCancel: setCancel,
247 MinVer: minVer,
248 MaxVer: maxVer,
249 SetSubVer: setSubVer,
250 Priority: priority,
251 Comment: comment,
252 StatusBar: statusBar,
253 Reserved: "",
254 }
255 }
256 257 // NewAlertFromPayload returns an Alert with values deserialized from the serialized payload.
258 func NewAlertFromPayload(serializedPayload []byte, pver uint32) (a *Alert, e error) {
259 var alert Alert
260 r := bytes.NewReader(serializedPayload)
261 if e = alert.Deserialize(r, pver); E.Chk(e) {
262 return
263 }
264 return &alert, nil
265 }
266 267 // MsgAlert implements the Message interface and defines a bitcoin alert message. This is a signed message that provides
268 // notifications that the client should display if the signature matches the key. bitcoind/bitcoin-qt only checks
269 // against a signature from the core developers.
270 type MsgAlert struct {
271 // SerializedPayload is the alert payload serialized as a string so that the version can change but the Alert can
272 // still be passed on by older clients.
273 SerializedPayload []byte
274 // Signature is the ECDSA signature of the message.
275 Signature []byte
276 // Deserialized Payload
277 Payload *Alert
278 }
279 280 // BtcDecode decodes r using the bitcoin protocol encoding into the receiver. This is part of the Message interface implementation.
281 func (msg *MsgAlert) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) (e error) {
282 if msg.SerializedPayload, e = ReadVarBytes(r, pver, MaxMessagePayload, "alert serialized payload"); E.Chk(e) {
283 return
284 }
285 if msg.Payload, e = NewAlertFromPayload(msg.SerializedPayload, pver); E.Chk(e) {
286 msg.Payload = nil
287 }
288 msg.Signature, e = ReadVarBytes(
289 r, pver, MaxMessagePayload, "alert signature",
290 )
291 return
292 }
293 294 // BtcEncode encodes the receiver to w using the bitcoin protocol encoding. This is part of the Message interface
295 // implementation.
296 func (msg *MsgAlert) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) (e error) {
297 var serializedpayload []byte
298 if msg.Payload != nil {
299 // try to Serialize Payload if possible
300 r := new(bytes.Buffer)
301 if e = msg.Payload.Serialize(r, pver); E.Chk(e) {
302 303 // Serialize failed - ignore & fallback to SerializedPayload
304 serializedpayload = msg.SerializedPayload
305 } else {
306 serializedpayload = r.Bytes()
307 }
308 } else {
309 serializedpayload = msg.SerializedPayload
310 }
311 slen := uint64(len(serializedpayload))
312 if slen == 0 {
313 return messageError("MsgAlert.BtcEncode", "empty serialized payload")
314 }
315 if e = WriteVarBytes(w, pver, serializedpayload); E.Chk(e) {
316 317 return
318 }
319 return WriteVarBytes(w, pver, msg.Signature)
320 }
321 322 // Command returns the protocol command string for the message. This is part of the Message interface implementation.
323 func (msg *MsgAlert) Command() string {
324 return CmdAlert
325 }
326 327 // MaxPayloadLength returns the maximum length the payload can be for the receiver. This is part of the Message
328 // interface implementation.
329 func (msg *MsgAlert) MaxPayloadLength(pver uint32) uint32 {
330 // Since this can vary depending on the message, make it the max size allowed.
331 return MaxMessagePayload
332 }
333 334 // NewMsgAlert returns a new bitcoin alert message that conforms to the Message interface. See MsgAlert for details.
335 func NewMsgAlert(serializedPayload []byte, signature []byte) *MsgAlert {
336 return &MsgAlert{
337 SerializedPayload: serializedPayload,
338 Signature: signature,
339 Payload: nil,
340 }
341 }
342