1 // Copyright (c) 2013-2015 The btcsuite developers
2 // Use of this source code is governed by an ISC
3 // license that can be found in the LICENSE file.
13 // MsgAlert contains a payload and a signature:
15 // ===============================================
16 // | Field | Data Type | Size |
17 // ===============================================
18 // | payload | []uchar | ? |
19 // -----------------------------------------------
20 // | signature | []uchar | ? |
21 // -----------------------------------------------
23 // Here payload is an Alert serialized into a byte array to ensure that
24 // versions using incompatible alert formats can still relay
25 // alerts among one another.
27 // An Alert is the payload deserialized as follows:
29 // ===============================================
30 // | Field | Data Type | Size |
31 // ===============================================
32 // | Version | int32 | 4 |
33 // -----------------------------------------------
34 // | RelayUntil | int64 | 8 |
35 // -----------------------------------------------
36 // | Expiration | int64 | 8 |
37 // -----------------------------------------------
39 // -----------------------------------------------
40 // | Cancel | int32 | 4 |
41 // -----------------------------------------------
42 // | SetCancel | set<int32> | ? |
43 // -----------------------------------------------
44 // | MinVer | int32 | 4 |
45 // -----------------------------------------------
46 // | MaxVer | int32 | 4 |
47 // -----------------------------------------------
48 // | SetSubVer | set<string> | ? |
49 // -----------------------------------------------
50 // | Priority | int32 | 4 |
51 // -----------------------------------------------
52 // | Comment | string | ? |
53 // -----------------------------------------------
54 // | StatusBar | string | ? |
55 // -----------------------------------------------
56 // | Reserved | string | ? |
57 // -----------------------------------------------
58 // | Total (Fixed) | 45 |
59 // -----------------------------------------------
62 // * string is a VarString i.e VarInt length followed by the string itself
63 // * set<string> is a VarInt followed by as many number of strings
64 // * set<int32> is a VarInt followed by as many number of ints
65 // * fixedAlertSize = 40 + 5*min(VarInt) = 40 + 5*1 = 45
67 // Now we can define bounds on Alert size, SetCancel and SetSubVer
69 // Fixed size of the alert payload
70 const fixedAlertSize = 45
72 // maxSignatureSize is the max size of an ECDSA signature.
73 // NOTE: Since this size is fixed and < 255, the size of VarInt required = 1.
74 const maxSignatureSize = 72
76 // maxAlertSize is the maximum size an alert.
78 // MessagePayload = VarInt(Alert) + Alert + VarInt(Signature) + Signature
79 // MaxMessagePayload = maxAlertSize + max(VarInt) + maxSignatureSize + 1
80 const maxAlertSize = MaxMessagePayload - maxSignatureSize - MaxVarIntPayload - 1
82 // maxCountSetCancel is the maximum number of cancel IDs that could possibly
83 // fit into a maximum size alert.
85 // maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
86 // for caculating maximum number of cancel IDs, set all other var sizes to 0
87 // maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(int32)
88 // x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
89 const maxCountSetCancel = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 4
91 // maxCountSetSubVer is the maximum number of subversions that could possibly
92 // fit into a maximum size alert.
94 // maxAlertSize = fixedAlertSize + max(SetCancel) + max(SetSubVer) + 3*(string)
95 // for caculating maximum number of subversions, set all other var sizes to 0
96 // maxAlertSize = fixedAlertSize + (MaxVarIntPayload-1) + x*sizeOf(string)
97 // x = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / sizeOf(string)
98 // subversion would typically be something like "/Satoshi:0.7.2/" (15 bytes)
99 // so assuming < 255 bytes, sizeOf(string) = sizeOf(uint8) + 255 = 256
100 const maxCountSetSubVer = (maxAlertSize - fixedAlertSize - MaxVarIntPayload + 1) / 256
102 // Alert contains the data deserialized from the MsgAlert payload.
104 // Alert format version
107 // Timestamp beyond which nodes should stop relaying this alert
110 // Timestamp beyond which this alert is no longer in effect and
114 // A unique ID number for this alert
117 // All alerts with an ID less than or equal to this number should
118 // cancelled, deleted and not accepted in the future
121 // All alert IDs contained in this set should be cancelled as above
124 // This alert only applies to versions greater than or equal to this
125 // version. Other versions should still relay it.
128 // This alert only applies to versions less than or equal to this version.
129 // Other versions should still relay it.
132 // If this set contains any elements, then only nodes that have their
133 // subVer contained in this set are affected by the alert. Other versions
134 // should still relay it.
137 // Relative priority compared to other alerts
140 // A comment on the alert that is not displayed
143 // The alert message that is displayed to the user
150 // Serialize encodes the alert to w using the alert protocol encoding format.
151 func (alert *Alert) Serialize(w io.Writer, pver uint32) error {
152 err := writeElements(w, alert.Version, alert.RelayUntil,
153 alert.Expiration, alert.ID, alert.Cancel)
158 count := len(alert.SetCancel)
159 if count > maxCountSetCancel {
160 str := fmt.Sprintf("too many cancel alert IDs for alert "+
161 "[count %v, max %v]", count, maxCountSetCancel)
162 return messageError("Alert.Serialize", str)
164 err = WriteVarInt(w, pver, uint64(count))
168 for i := 0; i < count; i++ {
169 err = writeElement(w, alert.SetCancel[i])
175 err = writeElements(w, alert.MinVer, alert.MaxVer)
180 count = len(alert.SetSubVer)
181 if count > maxCountSetSubVer {
182 str := fmt.Sprintf("too many sub versions for alert "+
183 "[count %v, max %v]", count, maxCountSetSubVer)
184 return messageError("Alert.Serialize", str)
186 err = WriteVarInt(w, pver, uint64(count))
190 for i := 0; i < count; i++ {
191 err = WriteVarString(w, pver, alert.SetSubVer[i])
197 err = writeElement(w, alert.Priority)
201 err = WriteVarString(w, pver, alert.Comment)
205 err = WriteVarString(w, pver, alert.StatusBar)
209 return WriteVarString(w, pver, alert.Reserved)
212 // Deserialize decodes from r into the receiver using the alert protocol
214 func (alert *Alert) Deserialize(r io.Reader, pver uint32) error {
215 err := readElements(r, &alert.Version, &alert.RelayUntil,
216 &alert.Expiration, &alert.ID, &alert.Cancel)
221 // SetCancel: first read a VarInt that contains
222 // count - the number of Cancel IDs, then
223 // iterate count times and read them
224 count, err := ReadVarInt(r, pver)
228 if count > maxCountSetCancel {
229 str := fmt.Sprintf("too many cancel alert IDs for alert "+
230 "[count %v, max %v]", count, maxCountSetCancel)
231 return messageError("Alert.Deserialize", str)
233 alert.SetCancel = make([]int32, count)
234 for i := 0; i < int(count); i++ {
235 err := readElement(r, &alert.SetCancel[i])
241 err = readElements(r, &alert.MinVer, &alert.MaxVer)
246 // SetSubVer: similar to SetCancel
247 // but read count number of sub-version strings
248 count, err = ReadVarInt(r, pver)
252 if count > maxCountSetSubVer {
253 str := fmt.Sprintf("too many sub versions for alert "+
254 "[count %v, max %v]", count, maxCountSetSubVer)
255 return messageError("Alert.Deserialize", str)
257 alert.SetSubVer = make([]string, count)
258 for i := 0; i < int(count); i++ {
259 alert.SetSubVer[i], err = ReadVarString(r, pver)
265 err = readElement(r, &alert.Priority)
269 alert.Comment, err = ReadVarString(r, pver)
273 alert.StatusBar, err = ReadVarString(r, pver)
277 alert.Reserved, err = ReadVarString(r, pver)
281 // NewAlert returns an new Alert with values provided.
282 func NewAlert(version int32, relayUntil int64, expiration int64,
283 id int32, cancel int32, setCancel []int32, minVer int32,
284 maxVer int32, setSubVer []string, priority int32, comment string,
285 statusBar string) *Alert {
288 RelayUntil: relayUntil,
289 Expiration: expiration,
292 SetCancel: setCancel,
295 SetSubVer: setSubVer,
298 StatusBar: statusBar,
303 // NewAlertFromPayload returns an Alert with values deserialized from the
304 // serialized payload.
305 func NewAlertFromPayload(serializedPayload []byte, pver uint32) (*Alert, error) {
307 r := bytes.NewReader(serializedPayload)
308 err := alert.Deserialize(r, pver)
315 // MsgAlert implements the Message interface and defines a bitcoin alert
318 // This is a signed message that provides notifications that the client should
319 // display if the signature matches the key. bitcoind/bitcoin-qt only checks
320 // against a signature from the core developers.
321 type MsgAlert struct {
322 // SerializedPayload is the alert payload serialized as a string so that the
323 // version can change but the Alert can still be passed on by older
325 SerializedPayload []byte
327 // Signature is the ECDSA signature of the message.
330 // Deserialized Payload
334 // BtcDecode decodes r using the bitcoin protocol encoding into the receiver.
335 // This is part of the Message interface implementation.
336 func (msg *MsgAlert) BtcDecode(r io.Reader, pver uint32, enc MessageEncoding) error {
339 msg.SerializedPayload, err = ReadVarBytes(r, pver, MaxMessagePayload,
340 "alert serialized payload")
345 msg.Payload, err = NewAlertFromPayload(msg.SerializedPayload, pver)
350 msg.Signature, err = ReadVarBytes(r, pver, MaxMessagePayload,
355 // BtcEncode encodes the receiver to w using the bitcoin protocol encoding.
356 // This is part of the Message interface implementation.
357 func (msg *MsgAlert) BtcEncode(w io.Writer, pver uint32, enc MessageEncoding) error {
359 var serializedpayload []byte
360 if msg.Payload != nil {
361 // try to Serialize Payload if possible
362 r := new(bytes.Buffer)
363 err = msg.Payload.Serialize(r, pver)
365 // Serialize failed - ignore & fallback
366 // to SerializedPayload
367 serializedpayload = msg.SerializedPayload
369 serializedpayload = r.Bytes()
372 serializedpayload = msg.SerializedPayload
374 slen := uint64(len(serializedpayload))
376 return messageError("MsgAlert.BtcEncode", "empty serialized payload")
378 err = WriteVarBytes(w, pver, serializedpayload)
382 return WriteVarBytes(w, pver, msg.Signature)
385 // Command returns the protocol command string for the message. This is part
386 // of the Message interface implementation.
387 func (msg *MsgAlert) Command() string {
391 // MaxPayloadLength returns the maximum length the payload can be for the
392 // receiver. This is part of the Message interface implementation.
393 func (msg *MsgAlert) MaxPayloadLength(pver uint32) uint32 {
394 // Since this can vary depending on the message, make it the max
396 return MaxMessagePayload
399 // NewMsgAlert returns a new bitcoin alert message that conforms to the Message
400 // interface. See MsgAlert for details.
401 func NewMsgAlert(serializedPayload []byte, signature []byte) *MsgAlert {
403 SerializedPayload: serializedPayload,
404 Signature: signature,