github.com/palcoin-project/palcd@v1.0.0/wire/msgalert.go (about) 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. 4 5 package wire 6 7 import ( 8 "bytes" 9 "fmt" 10 "io" 11 ) 12 13 // MsgAlert contains a payload and a signature: 14 // 15 // =============================================== 16 // | Field | Data Type | Size | 17 // =============================================== 18 // | payload | []uchar | ? | 19 // ----------------------------------------------- 20 // | signature | []uchar | ? | 21 // ----------------------------------------------- 22 // 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. 26 // 27 // An Alert is the payload deserialized as follows: 28 // 29 // =============================================== 30 // | Field | Data Type | Size | 31 // =============================================== 32 // | Version | int32 | 4 | 33 // ----------------------------------------------- 34 // | RelayUntil | int64 | 8 | 35 // ----------------------------------------------- 36 // | Expiration | int64 | 8 | 37 // ----------------------------------------------- 38 // | ID | int32 | 4 | 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 // ----------------------------------------------- 60 // 61 // NOTE: 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 66 // 67 // Now we can define bounds on Alert size, SetCancel and SetSubVer 68 69 // Fixed size of the alert payload 70 const fixedAlertSize = 45 71 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 75 76 // maxAlertSize is the maximum size an alert. 77 // 78 // MessagePayload = VarInt(Alert) + Alert + VarInt(Signature) + Signature 79 // MaxMessagePayload = maxAlertSize + max(VarInt) + maxSignatureSize + 1 80 const maxAlertSize = MaxMessagePayload - maxSignatureSize - MaxVarIntPayload - 1 81 82 // maxCountSetCancel is the maximum number of cancel IDs that could possibly 83 // fit into a maximum size alert. 84 // 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 90 91 // maxCountSetSubVer is the maximum number of subversions that could possibly 92 // fit into a maximum size alert. 93 // 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 101 102 // Alert contains the data deserialized from the MsgAlert payload. 103 type Alert struct { 104 // Alert format version 105 Version int32 106 107 // Timestamp beyond which nodes should stop relaying this alert 108 RelayUntil int64 109 110 // Timestamp beyond which this alert is no longer in effect and 111 // should be ignored 112 Expiration int64 113 114 // A unique ID number for this alert 115 ID int32 116 117 // All alerts with an ID less than or equal to this number should 118 // cancelled, deleted and not accepted in the future 119 Cancel int32 120 121 // All alert IDs contained in this set should be cancelled as above 122 SetCancel []int32 123 124 // This alert only applies to versions greater than or equal to this 125 // version. Other versions should still relay it. 126 MinVer int32 127 128 // This alert only applies to versions less than or equal to this version. 129 // Other versions should still relay it. 130 MaxVer int32 131 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. 135 SetSubVer []string 136 137 // Relative priority compared to other alerts 138 Priority int32 139 140 // A comment on the alert that is not displayed 141 Comment string 142 143 // The alert message that is displayed to the user 144 StatusBar string 145 146 // Reserved 147 Reserved string 148 } 149 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) 154 if err != nil { 155 return err 156 } 157 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) 163 } 164 err = WriteVarInt(w, pver, uint64(count)) 165 if err != nil { 166 return err 167 } 168 for i := 0; i < count; i++ { 169 err = writeElement(w, alert.SetCancel[i]) 170 if err != nil { 171 return err 172 } 173 } 174 175 err = writeElements(w, alert.MinVer, alert.MaxVer) 176 if err != nil { 177 return err 178 } 179 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) 185 } 186 err = WriteVarInt(w, pver, uint64(count)) 187 if err != nil { 188 return err 189 } 190 for i := 0; i < count; i++ { 191 err = WriteVarString(w, pver, alert.SetSubVer[i]) 192 if err != nil { 193 return err 194 } 195 } 196 197 err = writeElement(w, alert.Priority) 198 if err != nil { 199 return err 200 } 201 err = WriteVarString(w, pver, alert.Comment) 202 if err != nil { 203 return err 204 } 205 err = WriteVarString(w, pver, alert.StatusBar) 206 if err != nil { 207 return err 208 } 209 return WriteVarString(w, pver, alert.Reserved) 210 } 211 212 // Deserialize decodes from r into the receiver using the alert protocol 213 // encoding format. 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) 217 if err != nil { 218 return err 219 } 220 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) 225 if err != nil { 226 return err 227 } 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) 232 } 233 alert.SetCancel = make([]int32, count) 234 for i := 0; i < int(count); i++ { 235 err := readElement(r, &alert.SetCancel[i]) 236 if err != nil { 237 return err 238 } 239 } 240 241 err = readElements(r, &alert.MinVer, &alert.MaxVer) 242 if err != nil { 243 return err 244 } 245 246 // SetSubVer: similar to SetCancel 247 // but read count number of sub-version strings 248 count, err = ReadVarInt(r, pver) 249 if err != nil { 250 return err 251 } 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) 256 } 257 alert.SetSubVer = make([]string, count) 258 for i := 0; i < int(count); i++ { 259 alert.SetSubVer[i], err = ReadVarString(r, pver) 260 if err != nil { 261 return err 262 } 263 } 264 265 err = readElement(r, &alert.Priority) 266 if err != nil { 267 return err 268 } 269 alert.Comment, err = ReadVarString(r, pver) 270 if err != nil { 271 return err 272 } 273 alert.StatusBar, err = ReadVarString(r, pver) 274 if err != nil { 275 return err 276 } 277 alert.Reserved, err = ReadVarString(r, pver) 278 return err 279 } 280 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 { 286 return &Alert{ 287 Version: version, 288 RelayUntil: relayUntil, 289 Expiration: expiration, 290 ID: id, 291 Cancel: cancel, 292 SetCancel: setCancel, 293 MinVer: minVer, 294 MaxVer: maxVer, 295 SetSubVer: setSubVer, 296 Priority: priority, 297 Comment: comment, 298 StatusBar: statusBar, 299 Reserved: "", 300 } 301 } 302 303 // NewAlertFromPayload returns an Alert with values deserialized from the 304 // serialized payload. 305 func NewAlertFromPayload(serializedPayload []byte, pver uint32) (*Alert, error) { 306 var alert Alert 307 r := bytes.NewReader(serializedPayload) 308 err := alert.Deserialize(r, pver) 309 if err != nil { 310 return nil, err 311 } 312 return &alert, nil 313 } 314 315 // MsgAlert implements the Message interface and defines a bitcoin alert 316 // message. 317 // 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 324 // clients. 325 SerializedPayload []byte 326 327 // Signature is the ECDSA signature of the message. 328 Signature []byte 329 330 // Deserialized Payload 331 Payload *Alert 332 } 333 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 { 337 var err error 338 339 msg.SerializedPayload, err = ReadVarBytes(r, pver, MaxMessagePayload, 340 "alert serialized payload") 341 if err != nil { 342 return err 343 } 344 345 msg.Payload, err = NewAlertFromPayload(msg.SerializedPayload, pver) 346 if err != nil { 347 msg.Payload = nil 348 } 349 350 msg.Signature, err = ReadVarBytes(r, pver, MaxMessagePayload, 351 "alert signature") 352 return err 353 } 354 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 { 358 var err 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) 364 if err != nil { 365 // Serialize failed - ignore & fallback 366 // to SerializedPayload 367 serializedpayload = msg.SerializedPayload 368 } else { 369 serializedpayload = r.Bytes() 370 } 371 } else { 372 serializedpayload = msg.SerializedPayload 373 } 374 slen := uint64(len(serializedpayload)) 375 if slen == 0 { 376 return messageError("MsgAlert.BtcEncode", "empty serialized payload") 377 } 378 err = WriteVarBytes(w, pver, serializedpayload) 379 if err != nil { 380 return err 381 } 382 return WriteVarBytes(w, pver, msg.Signature) 383 } 384 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 { 388 return CmdAlert 389 } 390 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 395 // size allowed. 396 return MaxMessagePayload 397 } 398 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 { 402 return &MsgAlert{ 403 SerializedPayload: serializedPayload, 404 Signature: signature, 405 Payload: nil, 406 } 407 }