go-ethereum/peer.go

882 lines
21 KiB
Go
Raw Normal View History

2014-01-23 13:14:01 -06:00
package eth
import (
2014-02-13 08:12:16 -06:00
"bytes"
"container/list"
2014-02-10 04:45:08 -06:00
"fmt"
"math"
"math/big"
2014-01-23 13:14:01 -06:00
"net"
"strconv"
2014-01-31 13:01:28 -06:00
"strings"
2014-01-23 13:14:01 -06:00
"sync/atomic"
"time"
2014-07-26 04:24:44 -05:00
"github.com/ethereum/eth-go/ethchain"
"github.com/ethereum/eth-go/ethlog"
"github.com/ethereum/eth-go/ethutil"
"github.com/ethereum/eth-go/ethwire"
2014-01-23 13:14:01 -06:00
)
var peerlogger = ethlog.NewLogger("PEER")
2014-01-23 13:14:01 -06:00
const (
// The size of the output buffer for writing messages
outputBufferSize = 50
2014-03-03 04:34:04 -06:00
// Current protocol version
2014-10-08 05:00:03 -05:00
ProtocolVersion = 34
// Current P2P version
P2PVersion = 0
2014-09-17 18:02:15 -05:00
// Ethereum network version
NetVersion = 0
// Interval for ping/pong message
2014-06-14 17:04:18 -05:00
pingPongTimer = 2 * time.Second
2014-01-23 13:14:01 -06:00
)
2014-02-02 13:00:09 -06:00
type DiscReason byte
const (
// 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.
2014-10-02 10:03:48 -05:00
DiscRequested DiscReason = iota
DiscReTcpSysErr
DiscBadProto
DiscBadPeer
DiscTooManyPeers
DiscConnDup
DiscGenesisErr
DiscProtoErr
DiscQuitting
2014-02-02 13:00:09 -06:00
)
var discReasonToString = []string{
2014-07-01 13:32:47 -05:00
"requested",
"TCP sys error",
"bad protocol",
"useless peer",
"too many peers",
"already connected",
"wrong genesis block",
"incompatible network",
"quitting",
2014-02-02 13:00:09 -06:00
}
func (d DiscReason) String() string {
if len(discReasonToString) < int(d) {
2014-02-02 13:00:09 -06:00
return "Unknown"
}
return discReasonToString[d]
}
// Peer capabilities
2014-01-31 13:01:28 -06:00
type Caps byte
const (
2014-09-17 18:02:15 -05:00
CapPeerDiscTy Caps = 1 << iota
CapTxTy
CapChainTy
2014-02-02 09:15:39 -06:00
2014-02-02 13:00:09 -06:00
CapDefault = CapChainTy | CapTxTy | CapPeerDiscTy
2014-01-31 13:01:28 -06:00
)
var capsToString = map[Caps]string{
2014-02-02 13:00:09 -06:00
CapPeerDiscTy: "Peer discovery",
CapTxTy: "Transaction relaying",
CapChainTy: "Block chain relaying",
2014-01-31 13:01:28 -06:00
}
2014-02-06 06:27:57 -06:00
func (c Caps) IsCap(cap Caps) bool {
return c&cap > 0
}
2014-01-31 13:01:28 -06:00
func (c Caps) String() string {
var caps []string
2014-02-06 06:27:57 -06:00
if c.IsCap(CapPeerDiscTy) {
2014-02-02 13:00:09 -06:00
caps = append(caps, capsToString[CapPeerDiscTy])
2014-01-31 13:01:28 -06:00
}
2014-02-06 06:27:57 -06:00
if c.IsCap(CapChainTy) {
2014-01-31 13:01:28 -06:00
caps = append(caps, capsToString[CapChainTy])
}
2014-02-06 06:27:57 -06:00
if c.IsCap(CapTxTy) {
2014-01-31 13:01:28 -06:00
caps = append(caps, capsToString[CapTxTy])
}
return strings.Join(caps, " | ")
}
2014-01-23 13:14:01 -06:00
type Peer struct {
// Ethereum interface
ethereum *Ethereum
// Net connection
conn net.Conn
// Output queue which is used to communicate and handle messages
outputQueue chan *ethwire.Msg
// Quit channel
quit chan bool
// Determines whether it's an inbound or outbound peer
inbound bool
// Flag for checking the peer's connectivity state
connected int32
disconnect int32
// Last known message send
lastSend time.Time
// Indicated whether a verack has been send or not
// This flag is used by writeMessage to check if messages are allowed
// to be send or not. If no version is known all messages are ignored.
versionKnown bool
statusKnown bool
2014-01-23 13:14:01 -06:00
// Last received pong message
2014-10-01 18:36:59 -05:00
lastPong int64
lastBlockReceived time.Time
doneFetchingHashes bool
2014-01-30 16:48:52 -06:00
host []byte
port uint16
caps Caps
td *big.Int
bestHash []byte
lastReceivedHash []byte
requestedHashes [][]byte
2014-06-18 03:39:42 -05:00
// This peer's public key
pubkey []byte
2014-02-09 18:09:12 -06:00
// Indicated whether the node is catching up or not
2014-03-21 09:06:23 -05:00
catchingUp bool
diverted bool
2014-03-21 09:06:23 -05:00
blocksRequested int
2014-02-13 08:12:16 -06:00
2014-06-02 08:20:27 -05:00
version string
2014-06-03 03:42:55 -05:00
// We use this to give some kind of pingtime to a node, not very accurate, could be improved.
pingTime time.Duration
pingStartTime time.Time
lastRequestedBlock *ethchain.Block
protocolCaps *ethutil.Value
2014-01-23 13:14:01 -06:00
}
func NewPeer(conn net.Conn, ethereum *Ethereum, inbound bool) *Peer {
pubkey := ethereum.KeyManager().PublicKey()[1:]
2014-02-13 08:12:16 -06:00
2014-01-23 13:14:01 -06:00
return &Peer{
2014-10-01 18:36:59 -05:00
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
quit: make(chan bool),
ethereum: ethereum,
conn: conn,
inbound: inbound,
disconnect: 0,
connected: 1,
port: 30303,
pubkey: pubkey,
blocksRequested: 10,
caps: ethereum.ServerCaps(),
version: ethereum.ClientIdentity().String(),
protocolCaps: ethutil.NewValue(nil),
td: big.NewInt(0),
doneFetchingHashes: true,
2014-01-23 13:14:01 -06:00
}
}
2014-02-02 09:15:39 -06:00
func NewOutboundPeer(addr string, ethereum *Ethereum, caps Caps) *Peer {
2014-01-23 13:14:01 -06:00
p := &Peer{
2014-10-01 18:36:59 -05:00
outputQueue: make(chan *ethwire.Msg, outputBufferSize),
quit: make(chan bool),
ethereum: ethereum,
inbound: false,
connected: 0,
disconnect: 0,
port: 30303,
caps: caps,
version: ethereum.ClientIdentity().String(),
protocolCaps: ethutil.NewValue(nil),
td: big.NewInt(0),
doneFetchingHashes: true,
2014-01-23 13:14:01 -06:00
}
// Set up the connection in another goroutine so we don't block the main thread
go func() {
2014-07-24 05:30:04 -05:00
conn, err := p.Connect(addr)
2014-01-23 13:14:01 -06:00
if err != nil {
//peerlogger.Debugln("Connection to peer failed. Giving up.", err)
2014-01-23 13:14:01 -06:00
p.Stop()
2014-01-27 08:34:50 -06:00
return
2014-01-23 13:14:01 -06:00
}
p.conn = conn
// Atomically set the connection state
atomic.StoreInt32(&p.connected, 1)
atomic.StoreInt32(&p.disconnect, 0)
p.Start()
2014-01-23 13:14:01 -06:00
}()
return p
}
2014-07-24 05:30:04 -05:00
func (self *Peer) Connect(addr string) (conn net.Conn, err error) {
2014-07-26 04:24:44 -05:00
const maxTries = 3
for attempts := 0; attempts < maxTries; attempts++ {
2014-07-24 05:30:04 -05:00
conn, err = net.DialTimeout("tcp", addr, 10*time.Second)
if err != nil {
2014-07-26 04:24:44 -05:00
time.Sleep(time.Duration(attempts*20) * time.Second)
2014-07-24 05:30:04 -05:00
continue
}
// Success
return
}
return
}
2014-06-02 08:20:27 -05:00
// Getters
2014-06-03 03:42:55 -05:00
func (p *Peer) PingTime() string {
return p.pingTime.String()
}
2014-06-02 08:20:27 -05:00
func (p *Peer) Inbound() bool {
return p.inbound
}
func (p *Peer) LastSend() time.Time {
return p.lastSend
}
func (p *Peer) LastPong() int64 {
return p.lastPong
}
func (p *Peer) Host() []byte {
return p.host
}
func (p *Peer) Port() uint16 {
return p.port
}
func (p *Peer) Version() string {
return p.version
}
func (p *Peer) Connected() *int32 {
return &p.connected
}
// Setters
func (p *Peer) SetVersion(version string) {
p.version = version
}
2014-01-23 13:14:01 -06:00
// Outputs any RLP encoded data to the peer
func (p *Peer) QueueMessage(msg *ethwire.Msg) {
if atomic.LoadInt32(&p.connected) != 1 {
return
}
2014-01-23 13:14:01 -06:00
p.outputQueue <- msg
}
func (p *Peer) writeMessage(msg *ethwire.Msg) {
// Ignore the write if we're not connected
if atomic.LoadInt32(&p.connected) != 1 {
return
}
if !p.versionKnown {
switch msg.Type {
case ethwire.MsgHandshakeTy: // Ok
default: // Anything but ack is allowed
return
}
} else {
2014-09-15 08:42:12 -05:00
/*
if !p.statusKnown {
switch msg.Type {
case ethwire.MsgStatusTy: // Ok
default: // Anything but ack is allowed
return
}
}
2014-09-15 08:42:12 -05:00
*/
2014-01-23 13:14:01 -06:00
}
2014-09-17 08:58:02 -05:00
peerlogger.DebugDetailf("(%v) <= %v\n", p.conn.RemoteAddr(), formatMessage(msg))
2014-06-14 08:44:13 -05:00
2014-01-23 13:14:01 -06:00
err := ethwire.WriteMessage(p.conn, msg)
if err != nil {
peerlogger.Debugln(" Can't send message:", err)
2014-01-23 13:14:01 -06:00
// Stop the client if there was an error writing to it
p.Stop()
return
}
}
// Outbound message handler. Outbound messages are handled here
func (p *Peer) HandleOutbound() {
// The ping timer. Makes sure that every 2 minutes a ping is send to the peer
pingTimer := time.NewTicker(pingPongTimer)
2014-09-24 14:20:57 -05:00
serviceTimer := time.NewTicker(10 * time.Second)
2014-02-11 11:46:28 -06:00
2014-01-23 13:14:01 -06:00
out:
for {
2014-09-26 13:19:01 -05:00
skip:
2014-01-23 13:14:01 -06:00
select {
// Main message queue. All outbound messages are processed through here
case msg := <-p.outputQueue:
2014-09-17 18:02:15 -05:00
if !p.statusKnown {
switch msg.Type {
2014-09-30 16:26:52 -05:00
case ethwire.MsgGetTxsTy, ethwire.MsgTxTy, ethwire.MsgGetBlockHashesTy, ethwire.MsgBlockHashesTy, ethwire.MsgGetBlocksTy, ethwire.MsgBlockTy:
2014-09-26 13:19:01 -05:00
break skip
2014-09-17 18:02:15 -05:00
}
}
2014-01-23 13:14:01 -06:00
p.writeMessage(msg)
p.lastSend = time.Now()
// Ping timer
2014-02-02 09:15:39 -06:00
case <-pingTimer.C:
/*
timeSince := time.Since(time.Unix(p.lastPong, 0))
if !p.pingStartTime.IsZero() && p.lastPong != 0 && timeSince > (pingPongTimer+30*time.Second) {
peerlogger.Infof("Peer did not respond to latest pong fast enough, it took %s, disconnecting.\n", timeSince)
p.Stop()
return
}
*/
2014-01-24 10:48:21 -06:00
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
2014-06-03 03:42:55 -05:00
p.pingStartTime = time.Now()
2014-01-23 13:14:01 -06:00
2014-02-02 09:15:39 -06:00
// Service timer takes care of peer broadcasting, transaction
// posting or block posting
case <-serviceTimer.C:
2014-09-24 14:20:57 -05:00
p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
2014-02-02 09:15:39 -06:00
2014-01-23 13:14:01 -06:00
case <-p.quit:
2014-02-02 09:15:39 -06:00
// Break out of the for loop if a quit message is posted
2014-01-23 13:14:01 -06:00
break out
}
}
clean:
// This loop is for draining the output queue and anybody waiting for us
for {
select {
case <-p.outputQueue:
// TODO
default:
break clean
}
}
}
2014-09-17 08:58:02 -05:00
func formatMessage(msg *ethwire.Msg) (ret string) {
ret = fmt.Sprintf("%v %v", msg.Type, msg.Data)
2014-09-17 08:58:02 -05:00
/*
XXX Commented out because I need the log level here to determine
if i should or shouldn't generate this message
*/
/*
switch msg.Type {
case ethwire.MsgPeersTy:
ret += fmt.Sprintf("(%d entries)", msg.Data.Len())
case ethwire.MsgBlockTy:
b1, b2 := ethchain.NewBlockFromRlpValue(msg.Data.Get(0)), ethchain.NewBlockFromRlpValue(msg.Data.Get(msg.Data.Len()-1))
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), b1.Hash()[0:4], b2.Hash()[0:4])
case ethwire.MsgBlockHashesTy:
h1, h2 := msg.Data.Get(0).Bytes(), msg.Data.Get(msg.Data.Len()-1).Bytes()
ret += fmt.Sprintf("(%d entries) %x - %x", msg.Data.Len(), h1, h2)
}
*/
2014-09-17 08:58:02 -05:00
return
}
2014-01-23 13:14:01 -06:00
// Inbound handler. Inbound messages are received here and passed to the appropriate methods
func (p *Peer) HandleInbound() {
for atomic.LoadInt32(&p.disconnect) == 0 {
2014-06-03 03:42:55 -05:00
2014-02-09 18:09:12 -06:00
// HMM?
2014-07-15 13:36:11 -05:00
time.Sleep(50 * time.Millisecond)
2014-01-23 13:14:01 -06:00
// Wait for a message from the peer
2014-01-30 16:48:52 -06:00
msgs, err := ethwire.ReadMessages(p.conn)
if err != nil {
peerlogger.Debugln(err)
}
for _, msg := range msgs {
2014-09-17 08:58:02 -05:00
peerlogger.DebugDetailf("(%v) => %v\n", p.conn.RemoteAddr(), formatMessage(msg))
2014-06-14 08:44:13 -05:00
2014-01-30 16:48:52 -06:00
switch msg.Type {
case ethwire.MsgHandshakeTy:
// Version message
p.handleHandshake(msg)
2014-01-30 17:56:32 -06:00
//if p.caps.IsCap(CapPeerDiscTy) {
p.QueueMessage(ethwire.NewMessage(ethwire.MsgGetPeersTy, ""))
//}
2014-01-30 16:48:52 -06:00
case ethwire.MsgDiscTy:
p.Stop()
peerlogger.Infoln("Disconnect peer: ", DiscReason(msg.Data.Get(0).Uint()))
2014-01-30 16:48:52 -06:00
case ethwire.MsgPingTy:
// Respond back with pong
p.QueueMessage(ethwire.NewMessage(ethwire.MsgPongTy, ""))
case ethwire.MsgPongTy:
// If we received a pong back from a peer we set the
// last pong so the peer handler knows this peer is still
// active.
p.lastPong = time.Now().Unix()
p.pingTime = time.Since(p.pingStartTime)
2014-01-30 16:48:52 -06:00
case ethwire.MsgTxTy:
// If the message was a transaction queue the transaction
// in the TxPool where it will undergo validation and
// processing when a new block is found
2014-02-13 08:12:16 -06:00
for i := 0; i < msg.Data.Len(); i++ {
tx := ethchain.NewTransactionFromValue(msg.Data.Get(i))
p.ethereum.TxPool().QueueTransaction(tx)
2014-01-23 13:14:01 -06:00
}
2014-01-30 16:48:52 -06:00
case ethwire.MsgGetPeersTy:
// Peer asked for list of connected peers
p.pushPeers()
case ethwire.MsgPeersTy:
// Received a list of peers (probably because MsgGetPeersTy was send)
2014-07-24 05:04:15 -05:00
data := msg.Data
// Create new list of possible peers for the ethereum to process
peers := make([]string, data.Len())
// Parse each possible peer
for i := 0; i < data.Len(); i++ {
value := data.Get(i)
peers[i] = unpackAddr(value.Get(0), value.Get(1).Uint())
}
2014-01-30 16:48:52 -06:00
2014-07-24 05:04:15 -05:00
// Connect to the list of peers
p.ethereum.ProcessPeerList(peers)
case ethwire.MsgStatusTy:
// Handle peer's status msg
p.handleStatus(msg)
2014-09-17 18:02:15 -05:00
}
2014-09-17 18:02:15 -05:00
// TMP
if p.statusKnown {
switch msg.Type {
case ethwire.MsgGetTxsTy:
// Get the current transactions of the pool
txs := p.ethereum.TxPool().CurrentTransactions()
// Get the RlpData values from the txs
txsInterface := make([]interface{}, len(txs))
for i, tx := range txs {
txsInterface[i] = tx.RlpData()
}
// Broadcast it back to the peer
p.QueueMessage(ethwire.NewMessage(ethwire.MsgTxTy, txsInterface))
case ethwire.MsgGetBlockHashesTy:
if msg.Data.Len() < 2 {
peerlogger.Debugln("err: argument length invalid ", msg.Data.Len())
}
2014-01-27 08:34:50 -06:00
2014-09-17 18:02:15 -05:00
hash := msg.Data.Get(0).Bytes()
amount := msg.Data.Get(1).Uint()
2014-03-21 09:06:23 -05:00
2014-09-17 18:02:15 -05:00
hashes := p.ethereum.BlockChain().GetChainHashesFromHash(hash, amount)
2014-09-17 18:02:15 -05:00
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockHashesTy, ethutil.ByteSliceToInterface(hashes)))
2014-09-17 18:02:15 -05:00
case ethwire.MsgGetBlocksTy:
// Limit to max 300 blocks
max := int(math.Min(float64(msg.Data.Len()), 300.0))
var blocks []interface{}
2014-09-17 18:02:15 -05:00
for i := 0; i < max; i++ {
hash := msg.Data.Get(i).Bytes()
block := p.ethereum.BlockChain().GetBlock(hash)
if block != nil {
blocks = append(blocks, block.Value().Raw())
}
2014-01-30 16:48:52 -06:00
}
2014-09-17 18:02:15 -05:00
p.QueueMessage(ethwire.NewMessage(ethwire.MsgBlockTy, blocks))
2014-05-14 13:35:23 -05:00
2014-09-17 18:02:15 -05:00
case ethwire.MsgBlockHashesTy:
p.catchingUp = true
2014-08-21 07:52:21 -05:00
2014-09-17 18:02:15 -05:00
blockPool := p.ethereum.blockPool
2014-09-17 18:02:15 -05:00
foundCommonHash := false
2014-09-17 18:02:15 -05:00
it := msg.Data.NewIterator()
for it.Next() {
hash := it.Value().Bytes()
2014-10-01 18:36:59 -05:00
p.lastReceivedHash = hash
2014-09-17 18:02:15 -05:00
if blockPool.HasCommonHash(hash) {
foundCommonHash = true
2014-09-17 18:02:15 -05:00
break
}
blockPool.AddHash(hash, p)
2014-09-17 18:02:15 -05:00
}
2014-08-21 07:52:21 -05:00
2014-10-07 04:18:46 -05:00
if !foundCommonHash {
//if !p.FetchHashes() {
// p.doneFetchingHashes = true
//}
p.FetchHashes()
2014-10-01 18:36:59 -05:00
} else {
peerlogger.Infof("Found common hash (%x...)\n", p.lastReceivedHash[0:4])
p.doneFetchingHashes = true
2014-09-17 18:02:15 -05:00
}
2014-08-21 07:52:21 -05:00
2014-09-17 18:02:15 -05:00
case ethwire.MsgBlockTy:
p.catchingUp = true
2014-09-17 18:02:15 -05:00
blockPool := p.ethereum.blockPool
2014-08-21 07:52:21 -05:00
2014-09-17 18:02:15 -05:00
it := msg.Data.NewIterator()
for it.Next() {
block := ethchain.NewBlockFromRlpValue(it.Value())
2014-09-28 07:52:58 -05:00
blockPool.Add(block, p)
2014-01-25 10:13:33 -06:00
2014-09-17 18:02:15 -05:00
p.lastBlockReceived = time.Now()
}
2014-10-08 05:29:49 -05:00
case ethwire.MsgNewBlockTy:
2014-10-08 05:33:33 -05:00
var (
blockPool = p.ethereum.blockPool
block = ethchain.NewBlockFromRlpValue(msg.Data.Get(0))
td = msg.Data.Get(1).BigInt()
)
if td.Cmp(blockPool.td) > 0 {
p.ethereum.blockPool.AddNew(block, p)
}
}
2014-10-08 05:29:49 -05:00
2014-01-30 16:48:52 -06:00
}
2014-01-23 13:14:01 -06:00
}
}
2014-01-23 13:14:01 -06:00
p.Stop()
}
2014-09-24 04:41:57 -05:00
func (self *Peer) FetchBlocks(hashes [][]byte) {
if len(hashes) > 0 {
2014-10-01 18:36:59 -05:00
peerlogger.Debugf("Fetching blocks (%d)\n", len(hashes))
self.QueueMessage(ethwire.NewMessage(ethwire.MsgGetBlocksTy, ethutil.ByteSliceToInterface(hashes)))
}
}
func (self *Peer) FetchHashes() bool {
blockPool := self.ethereum.blockPool
2014-10-07 04:18:46 -05:00
return blockPool.FetchHashes(self)
}
2014-09-24 04:41:57 -05:00
func (self *Peer) FetchingHashes() bool {
2014-10-01 18:36:59 -05:00
return !self.doneFetchingHashes
2014-09-24 04:41:57 -05:00
}
// General update method
func (self *Peer) update() {
2014-09-24 04:55:02 -05:00
serviceTimer := time.NewTicker(100 * time.Millisecond)
out:
for {
select {
case <-serviceTimer.C:
2014-09-24 04:55:02 -05:00
if self.IsCap("eth") {
var (
sinceBlock = time.Since(self.lastBlockReceived)
)
2014-10-01 18:36:59 -05:00
if sinceBlock > 5*time.Second {
2014-09-24 04:55:02 -05:00
self.catchingUp = false
}
}
case <-self.quit:
break out
}
}
serviceTimer.Stop()
}
func (p *Peer) Start() {
2014-01-31 13:01:28 -06:00
peerHost, peerPort, _ := net.SplitHostPort(p.conn.LocalAddr().String())
servHost, servPort, _ := net.SplitHostPort(p.conn.RemoteAddr().String())
2014-01-30 17:56:32 -06:00
2014-01-31 13:01:28 -06:00
if p.inbound {
p.host, p.port = packAddr(peerHost, peerPort)
} else {
p.host, p.port = packAddr(servHost, servPort)
2014-01-30 17:56:32 -06:00
}
err := p.pushHandshake()
if err != nil {
peerlogger.Debugln("Peer can't send outbound version ack", err)
2014-01-30 17:56:32 -06:00
p.Stop()
return
2014-01-23 13:14:01 -06:00
}
go p.HandleOutbound()
// Run the inbound handler in a new goroutine
go p.HandleInbound()
// Run the general update handler
go p.update()
2014-01-30 16:48:52 -06:00
2014-06-03 03:42:55 -05:00
// Wait a few seconds for startup and then ask for an initial ping
time.Sleep(2 * time.Second)
p.writeMessage(ethwire.NewMessage(ethwire.MsgPingTy, ""))
p.pingStartTime = time.Now()
2014-01-23 13:14:01 -06:00
}
func (p *Peer) Stop() {
2014-10-02 10:03:48 -05:00
p.StopWithReason(DiscRequested)
}
func (p *Peer) StopWithReason(reason DiscReason) {
2014-01-23 13:14:01 -06:00
if atomic.AddInt32(&p.disconnect, 1) != 1 {
return
}
2014-10-02 10:03:48 -05:00
// Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
p.ethereum.RemovePeer(p)
2014-01-23 13:14:01 -06:00
close(p.quit)
if atomic.LoadInt32(&p.connected) != 0 {
2014-10-02 10:03:48 -05:00
p.writeMessage(ethwire.NewMessage(ethwire.MsgDiscTy, reason))
2014-01-23 13:14:01 -06:00
p.conn.Close()
}
}
2014-02-02 09:15:39 -06:00
func (p *Peer) peersMessage() *ethwire.Msg {
outPeers := make([]interface{}, len(p.ethereum.InOutPeers()))
2014-01-23 13:14:01 -06:00
// Serialise each peer
for i, peer := range p.ethereum.InOutPeers() {
// Don't return localhost as valid peer
if !net.ParseIP(peer.conn.RemoteAddr().String()).IsLoopback() {
outPeers[i] = peer.RlpData()
}
2014-01-23 13:14:01 -06:00
}
2014-02-02 09:15:39 -06:00
// Return the message to the peer with the known list of connected clients
return ethwire.NewMessage(ethwire.MsgPeersTy, outPeers)
}
2014-01-23 13:14:01 -06:00
2014-02-02 09:15:39 -06:00
// Pushes the list of outbound peers to the client when requested
func (p *Peer) pushPeers() {
p.QueueMessage(p.peersMessage())
2014-01-23 13:14:01 -06:00
}
func (self *Peer) pushStatus() {
msg := ethwire.NewMessage(ethwire.MsgStatusTy, []interface{}{
uint32(ProtocolVersion),
2014-09-17 18:02:15 -05:00
uint32(NetVersion),
2014-09-14 07:09:46 -05:00
self.ethereum.BlockChain().TD,
self.ethereum.BlockChain().CurrentBlock.Hash(),
self.ethereum.BlockChain().Genesis().Hash(),
})
self.QueueMessage(msg)
}
func (self *Peer) handleStatus(msg *ethwire.Msg) {
c := msg.Data
2014-09-14 07:09:46 -05:00
var (
protoVersion = c.Get(0).Uint()
netVersion = c.Get(1).Uint()
td = c.Get(2).BigInt()
bestHash = c.Get(3).Bytes()
genesis = c.Get(4).Bytes()
)
2014-09-17 08:58:02 -05:00
if bytes.Compare(self.ethereum.BlockChain().Genesis().Hash(), genesis) != 0 {
2014-09-17 18:02:15 -05:00
ethlogger.Warnf("Invalid genisis hash %x. Disabling [eth]\n", genesis)
return
}
if netVersion != NetVersion {
ethlogger.Warnf("Invalid network version %d. Disabling [eth]\n", netVersion)
return
}
if protoVersion != ProtocolVersion {
ethlogger.Warnf("Invalid protocol version %d. Disabling [eth]\n", protoVersion)
2014-09-17 08:58:02 -05:00
return
}
// Get the td and last hash
2014-09-14 07:09:46 -05:00
self.td = td
self.bestHash = bestHash
self.lastReceivedHash = bestHash
self.statusKnown = true
// Compare the total TD with the blockchain TD. If remote is higher
// fetch hashes from highest TD node.
self.FetchHashes()
2014-09-17 18:02:15 -05:00
ethlogger.Infof("Peer is [eth] capable. (TD = %v ~ %x) %d / %d", self.td, self.bestHash, protoVersion, netVersion)
2014-09-14 07:09:46 -05:00
}
2014-09-14 06:42:02 -05:00
func (p *Peer) pushHandshake() error {
pubkey := p.ethereum.KeyManager().PublicKey()
msg := ethwire.NewMessage(ethwire.MsgHandshakeTy, []interface{}{
2014-09-14 18:11:01 -05:00
P2PVersion, []byte(p.version), []interface{}{"eth"}, p.port, pubkey[1:],
2014-09-14 06:42:02 -05:00
})
p.QueueMessage(msg)
return nil
}
2014-01-23 13:14:01 -06:00
func (p *Peer) handleHandshake(msg *ethwire.Msg) {
c := msg.Data
var (
p2pVersion = c.Get(0).Uint()
clientId = c.Get(1).Str()
2014-09-16 09:36:27 -05:00
caps = c.Get(2)
port = c.Get(3).Uint()
pub = c.Get(4).Bytes()
)
// Check correctness of p2p protocol version
if p2pVersion != P2PVersion {
peerlogger.Debugf("Invalid P2P version. Require protocol %d, received %d\n", P2PVersion, p2pVersion)
p.Stop()
return
}
// Handle the pub key (validation, uniqueness)
2014-09-10 04:39:11 -05:00
if len(pub) == 0 {
peerlogger.Warnln("Pubkey required, not supplied in handshake.")
p.Stop()
return
}
2014-09-24 04:55:02 -05:00
// Self connect detection
pubkey := p.ethereum.KeyManager().PublicKey()
if bytes.Compare(pubkey[1:], pub) == 0 {
p.Stop()
return
}
2014-10-02 10:03:48 -05:00
// Check for blacklisting
for _, pk := range p.ethereum.blacklist {
if bytes.Compare(pk, pub) == 0 {
peerlogger.Debugf("Blacklisted peer tried to connect (%x...)\n", pubkey[0:4])
p.StopWithReason(DiscBadPeer)
return
}
}
usedPub := 0
// This peer is already added to the peerlist so we expect to find a double pubkey at least once
eachPeer(p.ethereum.Peers(), func(peer *Peer, e *list.Element) {
if bytes.Compare(pub, peer.pubkey) == 0 {
usedPub++
}
})
if usedPub > 0 {
peerlogger.Debugf("Pubkey %x found more then once. Already connected to client.", p.pubkey)
p.Stop()
return
}
p.pubkey = pub
2014-01-23 13:14:01 -06:00
// If this is an inbound connection send an ack back
if p.inbound {
p.port = uint16(port)
2014-02-13 08:12:16 -06:00
}
2014-09-24 04:55:02 -05:00
p.SetVersion(clientId)
2014-02-09 16:58:59 -06:00
p.versionKnown = true
p.ethereum.PushPeer(p)
p.ethereum.reactor.Post("peerList", p.ethereum.Peers())
p.protocolCaps = caps
2014-09-16 09:36:27 -05:00
capsIt := caps.NewIterator()
2014-09-17 08:58:02 -05:00
var capsStrs []string
2014-09-16 09:36:27 -05:00
for capsIt.Next() {
2014-09-17 08:58:02 -05:00
cap := capsIt.Value().Str()
switch cap {
2014-09-16 09:36:27 -05:00
case "eth":
p.pushStatus()
}
2014-09-17 08:58:02 -05:00
capsStrs = append(capsStrs, cap)
2014-09-16 09:36:27 -05:00
}
2014-09-17 08:58:02 -05:00
ethlogger.Infof("Added peer (%s) %d / %d (%v)\n", p.conn.RemoteAddr(), p.ethereum.Peers().Len(), p.ethereum.MaxPeers, capsStrs)
peerlogger.Debugln(p)
2014-02-13 08:12:16 -06:00
}
func (self *Peer) IsCap(cap string) bool {
capsIt := self.protocolCaps.NewIterator()
for capsIt.Next() {
if capsIt.Value().Str() == cap {
return true
}
}
return false
}
2014-09-26 06:45:26 -05:00
func (self *Peer) Caps() *ethutil.Value {
return self.protocolCaps
}
2014-02-13 08:12:16 -06:00
func (p *Peer) String() string {
var strBoundType string
if p.inbound {
strBoundType = "inbound"
} else {
strBoundType = "outbound"
}
var strConnectType string
if atomic.LoadInt32(&p.disconnect) == 0 {
strConnectType = "connected"
} else {
strConnectType = "disconnected"
2014-01-23 13:14:01 -06:00
}
2014-01-30 16:48:52 -06:00
2014-06-02 08:20:27 -05:00
return fmt.Sprintf("[%s] (%s) %v %s [%s]", strConnectType, strBoundType, p.conn.RemoteAddr(), p.version, p.caps)
2014-01-30 17:56:32 -06:00
2014-01-31 13:01:28 -06:00
}
2014-02-09 18:09:12 -06:00
2014-01-31 13:01:28 -06:00
func (p *Peer) RlpData() []interface{} {
return []interface{}{p.host, p.port, p.pubkey}
2014-01-23 13:14:01 -06:00
}
2014-05-01 15:13:59 -05:00
2014-10-08 05:00:03 -05:00
func packAddr(address, _port string) (host []byte, port uint16) {
p, _ := strconv.Atoi(_port)
port = uint16(p)
2014-05-01 15:13:59 -05:00
2014-10-08 05:00:03 -05:00
h := net.ParseIP(address)
if ip := h.To4(); ip != nil {
host = []byte(ip)
} else {
host = []byte(h)
}
return
2014-05-01 15:13:59 -05:00
}
func unpackAddr(value *ethutil.Value, p uint64) string {
2014-10-08 05:00:03 -05:00
host, _ := net.IP(value.Bytes()).MarshalText()
prt := strconv.Itoa(int(p))
return net.JoinHostPort(string(host), prt)
2014-05-01 15:13:59 -05:00
}