msgalert.go raw

   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