messaging.go 4.41 KB
Newer Older
obscuren's avatar
obscuren committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
package ethwire

import (
	"bytes"
	"errors"
	"fmt"
	"github.com/ethereum/eth-go/ethutil"
	"net"
	"time"
)

// Message:
// [4 bytes token] RLP([TYPE, DATA])
// Refer to http://wiki.ethereum.org/index.php/Wire_Protocol

// The magic token which should be the first 4 bytes of every message.
var MagicToken = []byte{34, 64, 8, 145}

type MsgType byte

const (
22 23 24
	// Values are given explicitly instead of by iota because these values are
	// defined by the wire protocol spec; it is easier for humans to ensure
	// correctness when values are explicit.
obscuren's avatar
obscuren committed
25 26 27 28 29 30 31 32 33 34
	MsgHandshakeTy  = 0x00
	MsgDiscTy       = 0x01
	MsgPingTy       = 0x02
	MsgPongTy       = 0x03
	MsgGetPeersTy   = 0x10
	MsgPeersTy      = 0x11
	MsgTxTy         = 0x12
	MsgBlockTy      = 0x13
	MsgGetChainTy   = 0x14
	MsgNotInChainTy = 0x15
35
	MsgGetTxsTy     = 0x16
obscuren's avatar
obscuren committed
36 37 38 39 40 41 42 43 44 45 46 47 48 49

	MsgTalkTy = 0xff
)

var msgTypeToString = map[MsgType]string{
	MsgHandshakeTy:  "Handshake",
	MsgDiscTy:       "Disconnect",
	MsgPingTy:       "Ping",
	MsgPongTy:       "Pong",
	MsgGetPeersTy:   "Get peers",
	MsgPeersTy:      "Peers",
	MsgTxTy:         "Transactions",
	MsgBlockTy:      "Blocks",
	MsgGetChainTy:   "Get chain",
50
	MsgGetTxsTy:     "Get Txs",
obscuren's avatar
obscuren committed
51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
	MsgNotInChainTy: "Not in chain",
}

func (mt MsgType) String() string {
	return msgTypeToString[mt]
}

type Msg struct {
	Type MsgType // Specifies how the encoded data should be interpreted
	//Data []byte
	Data *ethutil.Value
}

func NewMessage(msgType MsgType, data interface{}) *Msg {
	return &Msg{
		Type: msgType,
		Data: ethutil.NewValue(data),
	}
}

func ReadMessage(data []byte) (msg *Msg, remaining []byte, done bool, err error) {
	if len(data) == 0 {
		return nil, nil, true, nil
	}

	if len(data) <= 8 {
		return nil, remaining, false, errors.New("Invalid message")
	}

	// Check if the received 4 first bytes are the magic token
	if bytes.Compare(MagicToken, data[:4]) != 0 {
		return nil, nil, false, fmt.Errorf("MagicToken mismatch. Received %v", data[:4])
	}

	messageLength := ethutil.BytesToNumber(data[4:8])
	remaining = data[8+messageLength:]
	if int(messageLength) > len(data[8:]) {
		return nil, nil, false, fmt.Errorf("message length %d, expected %d", len(data[8:]), messageLength)
	}

	message := data[8 : 8+messageLength]
	decoder := ethutil.NewValueFromBytes(message)
	// Type of message
	t := decoder.Get(0).Uint()
	// Actual data
	d := decoder.SliceFrom(1)

	msg = &Msg{
		Type: MsgType(t),
		Data: d,
	}

	return
}

func bufferedRead(conn net.Conn) ([]byte, error) {
	return nil, nil
}

// The basic message reader waits for data on the given connection, decoding
// and doing a few sanity checks such as if there's a data type and
// unmarhals the given data
func ReadMessages(conn net.Conn) (msgs []*Msg, err error) {
	// The recovering function in case anything goes horribly wrong
	defer func() {
		if r := recover(); r != nil {
			err = fmt.Errorf("ethwire.ReadMessage error: %v", r)
		}
	}()

	// Buff for writing network message to
	//buff := make([]byte, 1440)
	var buff []byte
	var totalBytes int
	for {
		// Give buffering some time
		conn.SetReadDeadline(time.Now().Add(20 * time.Millisecond))
		// Create a new temporarily buffer
		b := make([]byte, 1440)
		// Wait for a message from this peer
		n, _ := conn.Read(b)
		if err != nil && n == 0 {
			if err.Error() != "EOF" {
				fmt.Println("err now", err)
				return nil, err
			} else {
				fmt.Println("IOF NOW")
				break
			}

			// Messages can't be empty
		} else if n == 0 {
			break
		}

		buff = append(buff, b[:n]...)
		totalBytes += n
	}

	// Reslice buffer
	buff = buff[:totalBytes]
	msg, remaining, done, err := ReadMessage(buff)
	for ; done != true; msg, remaining, done, err = ReadMessage(remaining) {
		//log.Println("rx", msg)

		if msg != nil {
			msgs = append(msgs, msg)
		}
	}

	return
}

// The basic message writer takes care of writing data over the given
// connection and does some basic error checking
func WriteMessage(conn net.Conn, msg *Msg) error {
	var pack []byte

	// Encode the type and the (RLP encoded) data for sending over the wire
	encoded := ethutil.NewValue(append([]interface{}{byte(msg.Type)}, msg.Data.Slice()...)).Encode()
	payloadLength := ethutil.NumberToBytes(uint32(len(encoded)), 32)

	// Write magic token and payload length (first 8 bytes)
	pack = append(MagicToken, payloadLength...)
	pack = append(pack, encoded...)
	//fmt.Printf("payload %v (%v) %q\n", msg.Type, conn.RemoteAddr(), encoded)

	// Write to the connection
	_, err := conn.Write(pack)
	if err != nil {
		return err
	}

	return nil
}