2014-01-23 13:14:01 -06:00
package eth
import (
2014-02-13 08:12:16 -06:00
"bytes"
2014-06-18 06:06:48 -05:00
"container/list"
2014-02-10 04:45:08 -06:00
"fmt"
2014-08-21 07:47:58 -05:00
"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
)
2014-06-23 06:54:10 -05: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-08-21 11:15:09 -05:00
ProtocolVersion = 28
2014-06-10 06:51:34 -05:00
// 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 (
2014-02-18 16:24:44 -06:00
// 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-02-02 13:00:09 -06:00
DiscReRequested = 0x00
DiscReTcpSysErr = 0x01
DiscBadProto = 0x02
DiscBadPeer = 0x03
DiscTooManyPeers = 0x04
2014-02-10 04:36:49 -06:00
DiscConnDup = 0x05
DiscGenesisErr = 0x06
DiscProtoErr = 0x07
2014-07-01 13:32:47 -05:00
DiscQuitting = 0x08
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 {
2014-03-05 03:42:51 -06:00
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-02-18 16:20:41 -06:00
CapPeerDiscTy = 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
// Last received pong message
2014-07-24 10:10:54 -05:00
lastPong int64
lastBlockReceived time . Time
2014-01-30 16:48:52 -06:00
2014-08-21 07:47:58 -05:00
host [ ] byte
port uint16
caps Caps
td * big . Int
bestHash [ ] byte
lastReceivedHash [ ] byte
requestedHashes [ ] [ ] byte
2014-02-09 16:34:33 -06:00
2014-06-18 03:39:42 -05:00
// This peer's public key
2014-02-09 16:34:33 -06:00
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
2014-05-20 04:50:34 -05:00
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
2014-06-23 04:24:45 -05:00
lastRequestedBlock * ethchain . Block
2014-01-23 13:14:01 -06:00
}
func NewPeer ( conn net . Conn , ethereum * Ethereum , inbound bool ) * Peer {
2014-06-29 12:30:05 -05:00
pubkey := ethereum . KeyManager ( ) . PublicKey ( ) [ 1 : ]
2014-02-13 08:12:16 -06:00
2014-01-23 13:14:01 -06:00
return & Peer {
2014-03-21 09:06:23 -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 ,
2014-05-12 06:39:37 -05:00
caps : ethereum . ServerCaps ( ) ,
2014-07-03 11:30:37 -05:00
version : ethereum . ClientIdentity ( ) . String ( ) ,
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 {
outputQueue : make ( chan * ethwire . Msg , outputBufferSize ) ,
quit : make ( chan bool ) ,
ethereum : ethereum ,
inbound : false ,
connected : 0 ,
disconnect : 0 ,
2014-02-02 09:15:39 -06:00
caps : caps ,
2014-07-03 11:30:37 -05:00
version : ethereum . ClientIdentity ( ) . String ( ) ,
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 {
2014-07-24 05:25:41 -05:00
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 )
2014-02-02 12:46:37 -06:00
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
//peerlogger.Debugf("Peer connection failed. Retrying (%d/%d) (%s)\n", attempts+1, maxTries, addr)
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 ) {
2014-05-14 04:29:57 -05:00
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
}
}
2014-07-10 08:03:26 -05:00
peerlogger . DebugDetailf ( "(%v) <= %v %v\n" , p . conn . RemoteAddr ( ) , msg . Type , msg . Data )
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 {
2014-06-23 06:54:10 -05:00
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
2014-06-10 06:51:34 -05:00
pingTimer := time . NewTicker ( pingPongTimer )
2014-02-02 13:06:37 -06:00
serviceTimer := time . NewTicker ( 5 * time . Minute )
2014-02-11 11:46:28 -06:00
2014-01-23 13:14:01 -06:00
out :
for {
select {
// Main message queue. All outbound messages are processed through here
case msg := <- p . outputQueue :
p . writeMessage ( msg )
p . lastSend = time . Now ( )
2014-06-10 06:51:34 -05:00
// Ping timer
2014-02-02 09:15:39 -06:00
case <- pingTimer . C :
2014-07-30 04:26:38 -05:00
/ *
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-02-02 13:00:09 -06:00
if p . caps & CapPeerDiscTy > 0 {
2014-02-02 09:15:39 -06:00
msg := p . peersMessage ( )
p . ethereum . BroadcastMsg ( msg )
}
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
}
}
}
// 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 )
2014-02-09 16:34:33 -06:00
if err != nil {
2014-06-23 06:54:10 -05:00
peerlogger . Debugln ( err )
2014-02-09 16:34:33 -06:00
}
for _ , msg := range msgs {
2014-07-10 08:03:26 -05:00
peerlogger . DebugDetailf ( "(%v) => %v %v\n" , p . conn . RemoteAddr ( ) , msg . Type , msg . Data )
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
2014-02-06 06:27:57 -06:00
if p . caps . IsCap ( CapPeerDiscTy ) {
p . QueueMessage ( ethwire . NewMessage ( ethwire . MsgGetPeersTy , "" ) )
}
2014-08-21 07:47:58 -05:00
2014-01-30 16:48:52 -06:00
case ethwire . MsgDiscTy :
p . Stop ( )
2014-07-30 04:26:38 -05:00
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 ( )
2014-07-30 04:26:38 -05:00
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 ++ {
2014-03-28 05:20:07 -05:00
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 )
2014-08-21 07:47:58 -05:00
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-08-21 07:47:58 -05:00
hash := msg . Data . Get ( 0 ) . Bytes ( )
amount := msg . Data . Get ( 1 ) . Uint ( )
2014-03-21 09:06:23 -05:00
2014-08-21 07:47:58 -05:00
hashes := p . ethereum . BlockChain ( ) . GetChainHashesFromHash ( hash , amount )
p . QueueMessage ( ethwire . NewMessage ( ethwire . MsgBlockHashesTy , ethutil . ByteSliceToInterface ( hashes ) ) )
case ethwire . MsgGetBlocksTy :
// Limit to max 300 blocks
max := int ( math . Min ( float64 ( msg . Data . Len ( ) ) , 300.0 ) )
var blocks [ ] interface { }
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-08-21 07:47:58 -05:00
p . QueueMessage ( ethwire . NewMessage ( ethwire . MsgBlockTy , blocks ) )
2014-05-14 13:35:23 -05:00
2014-08-21 07:47:58 -05:00
case ethwire . MsgBlockHashesTy :
2014-08-21 07:52:21 -05:00
p . catchingUp = true
2014-08-21 07:47:58 -05:00
blockPool := p . ethereum . blockPool
foundCommonHash := false
it := msg . Data . NewIterator ( )
for it . Next ( ) {
hash := it . Value ( ) . Bytes ( )
if blockPool . HasCommonHash ( hash ) {
foundCommonHash = true
break
2014-05-30 04:48:23 -05:00
}
2014-08-21 07:47:58 -05:00
blockPool . AddHash ( hash )
p . lastReceivedHash = hash
2014-08-21 07:52:21 -05:00
p . lastBlockReceived = time . Now ( )
2014-01-30 16:48:52 -06:00
}
2014-08-21 07:47:58 -05:00
if foundCommonHash {
p . FetchBlocks ( )
} else {
p . FetchHashes ( )
2014-05-20 04:50:34 -05:00
}
2014-08-21 07:52:21 -05:00
2014-08-21 07:47:58 -05:00
case ethwire . MsgBlockTy :
2014-08-21 07:52:21 -05:00
p . catchingUp = true
2014-08-21 07:47:58 -05:00
blockPool := p . ethereum . blockPool
it := msg . Data . NewIterator ( )
for it . Next ( ) {
block := ethchain . NewBlockFromRlpValue ( it . Value ( ) )
2014-08-21 07:52:21 -05:00
2014-08-21 07:47:58 -05:00
blockPool . SetBlock ( block )
2014-08-21 07:52:21 -05:00
p . lastBlockReceived = time . Now ( )
2014-03-28 05:20:07 -05:00
}
2014-01-25 10:13:33 -06:00
2014-08-21 07:47:58 -05:00
linked := blockPool . CheckLinkAndProcess ( func ( block * ethchain . Block ) {
p . ethereum . StateManager ( ) . Process ( block , false )
} )
if ! linked {
p . FetchBlocks ( )
}
2014-01-30 16:48:52 -06:00
}
2014-01-23 13:14:01 -06:00
}
}
2014-08-21 07:47:58 -05:00
2014-01-23 13:14:01 -06:00
p . Stop ( )
}
2014-08-21 07:47:58 -05:00
func ( self * Peer ) FetchBlocks ( ) {
blockPool := self . ethereum . blockPool
hashes := blockPool . Take ( 100 , self )
if len ( hashes ) > 0 {
self . QueueMessage ( ethwire . NewMessage ( ethwire . MsgGetBlocksTy , ethutil . ByteSliceToInterface ( hashes ) ) )
}
}
func ( self * Peer ) FetchHashes ( ) {
blockPool := self . ethereum . blockPool
if self . td . Cmp ( blockPool . td ) >= 0 {
peerlogger . Debugf ( "Requesting hashes from %x\n" , self . lastReceivedHash )
if ! blockPool . HasLatestHash ( ) {
self . QueueMessage ( ethwire . NewMessage ( ethwire . MsgGetBlockHashesTy , [ ] interface { } { self . lastReceivedHash , uint32 ( 200 ) } ) )
}
}
}
2014-07-24 10:10:54 -05:00
// General update method
func ( self * Peer ) update ( ) {
serviceTimer := time . NewTicker ( 5 * time . Second )
out :
for {
select {
case <- serviceTimer . C :
if time . Since ( self . lastBlockReceived ) > 10 * time . Second {
self . catchingUp = false
}
case <- self . quit :
break out
}
}
serviceTimer . Stop ( )
}
2014-02-02 12:46:37 -06:00
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 {
2014-06-23 06:54:10 -05:00
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 ( )
2014-07-24 10:10:54 -05:00
// 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 ( ) {
if atomic . AddInt32 ( & p . disconnect , 1 ) != 1 {
return
}
close ( p . quit )
if atomic . LoadInt32 ( & p . connected ) != 0 {
2014-01-25 10:13:33 -06:00
p . writeMessage ( ethwire . NewMessage ( ethwire . MsgDiscTy , "" ) )
2014-01-23 13:14:01 -06:00
p . conn . Close ( )
}
2014-05-12 09:30:21 -05:00
// Pre-emptively remove the peer; don't wait for reaping. We already know it's dead if we are here
2014-05-30 06:04:08 -05:00
p . ethereum . RemovePeer ( p )
2014-01-23 13:14:01 -06:00
}
func ( p * Peer ) pushHandshake ( ) error {
2014-06-29 12:30:05 -05:00
pubkey := p . ethereum . KeyManager ( ) . PublicKey ( )
msg := ethwire . NewMessage ( ethwire . MsgHandshakeTy , [ ] interface { } {
uint32 ( ProtocolVersion ) , uint32 ( 0 ) , [ ] byte ( p . version ) , byte ( p . caps ) , p . port , pubkey [ 1 : ] ,
2014-08-21 07:47:58 -05:00
p . ethereum . BlockChain ( ) . TD . Uint64 ( ) , p . ethereum . BlockChain ( ) . CurrentBlock . Hash ( ) ,
2014-06-29 12:30:05 -05:00
} )
2014-01-23 13:14:01 -06:00
2014-06-29 12:30:05 -05:00
p . QueueMessage ( msg )
2014-01-23 13:14:01 -06:00
return nil
}
2014-02-02 09:15:39 -06:00
func ( p * Peer ) peersMessage ( ) * ethwire . Msg {
2014-01-31 04:57:56 -06:00
outPeers := make ( [ ] interface { } , len ( p . ethereum . InOutPeers ( ) ) )
2014-01-23 13:14:01 -06:00
// Serialise each peer
2014-01-31 04:57:56 -06:00
for i , peer := range p . ethereum . InOutPeers ( ) {
2014-05-12 08:43:10 -05:00
// 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 ( p * Peer ) handleHandshake ( msg * ethwire . Msg ) {
2014-01-23 15:32:50 -06:00
c := msg . Data
2014-02-09 16:34:33 -06:00
2014-06-18 06:06:48 -05:00
// Set pubkey
p . pubkey = c . Get ( 5 ) . Bytes ( )
if p . pubkey == nil {
2014-06-23 06:54:10 -05:00
peerlogger . Warnln ( "Pubkey required, not supplied in handshake." )
2014-06-18 06:06:48 -05:00
p . Stop ( )
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 ( p . pubkey , peer . pubkey ) == 0 {
usedPub ++
}
} )
if usedPub > 0 {
2014-06-23 06:54:10 -05:00
peerlogger . Debugf ( "Pubkey %x found more then once. Already connected to client." , p . pubkey )
2014-06-18 06:06:48 -05:00
p . Stop ( )
return
}
2014-03-03 04:34:04 -06:00
if c . Get ( 0 ) . Uint ( ) != ProtocolVersion {
2014-06-23 06:54:10 -05:00
peerlogger . Debugf ( "Invalid peer version. Require protocol: %d. Received: %d\n" , ProtocolVersion , c . Get ( 0 ) . Uint ( ) )
2014-02-09 16:34:33 -06:00
p . Stop ( )
return
}
2014-02-09 18:09:12 -06:00
// [PROTOCOL_VERSION, NETWORK_ID, CLIENT_ID, CAPS, PORT, PUBKEY]
2014-01-23 13:14:01 -06:00
p . versionKnown = true
// If this is an inbound connection send an ack back
if p . inbound {
2014-02-13 08:12:16 -06:00
p . port = uint16 ( c . Get ( 4 ) . Uint ( ) )
2014-01-23 13:14:01 -06:00
2014-02-09 18:09:12 -06:00
// Self connect detection
2014-06-29 12:30:05 -05:00
pubkey := p . ethereum . KeyManager ( ) . PublicKey ( )
if bytes . Compare ( pubkey , p . pubkey ) == 0 {
2014-02-13 08:12:16 -06:00
p . Stop ( )
2014-02-09 16:58:59 -06:00
2014-02-13 08:12:16 -06:00
return
}
}
2014-02-09 16:58:59 -06:00
2014-02-13 08:12:16 -06:00
// Set the peer's caps
p . caps = Caps ( c . Get ( 3 ) . Byte ( ) )
2014-06-02 08:20:27 -05:00
2014-02-13 08:12:16 -06:00
// Get a reference to the peers version
2014-06-02 08:20:27 -05:00
versionString := c . Get ( 2 ) . Str ( )
if len ( versionString ) > 0 {
p . SetVersion ( c . Get ( 2 ) . Str ( ) )
}
2014-01-30 16:48:52 -06:00
2014-08-21 07:47:58 -05:00
// Get the td and last hash
p . td = c . Get ( 6 ) . BigInt ( )
p . bestHash = c . Get ( 7 ) . Bytes ( )
p . lastReceivedHash = p . bestHash
2014-06-18 06:06:48 -05:00
p . ethereum . PushPeer ( p )
p . ethereum . reactor . Post ( "peerList" , p . ethereum . Peers ( ) )
2014-08-21 07:47:58 -05:00
ethlogger . Infof ( "Added peer (%s) %d / %d (TD = %v ~ %x)\n" , p . conn . RemoteAddr ( ) , p . ethereum . Peers ( ) . Len ( ) , p . ethereum . MaxPeers , p . td , p . bestHash )
2014-06-18 06:06:48 -05:00
2014-07-24 05:11:30 -05:00
/ *
// Catch up with the connected peer
if ! p . ethereum . IsUpToDate ( ) {
peerlogger . Debugln ( "Already syncing up with a peer; sleeping" )
time . Sleep ( 10 * time . Second )
}
* /
2014-08-21 07:47:58 -05:00
//p.SyncWithPeerToLastKnown()
if p . td . Cmp ( p . ethereum . BlockChain ( ) . TD ) > 0 {
p . ethereum . blockPool . AddHash ( p . lastReceivedHash )
p . FetchHashes ( )
}
2014-05-20 04:50:34 -05:00
2014-06-23 06:54:10 -05:00
peerlogger . Debugln ( p )
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-05-20 04:50:34 -05:00
func ( p * Peer ) SyncWithPeerToLastKnown ( ) {
p . catchingUp = false
p . CatchupWithPeer ( p . ethereum . BlockChain ( ) . CurrentBlock . Hash ( ) )
}
2014-03-21 09:06:23 -05:00
2014-05-20 04:50:34 -05:00
func ( p * Peer ) FindCommonParentBlock ( ) {
if p . catchingUp {
return
}
2014-03-21 09:06:23 -05:00
2014-05-20 04:50:34 -05:00
p . catchingUp = true
if p . blocksRequested == 0 {
p . blocksRequested = 20
}
blocks := p . ethereum . BlockChain ( ) . GetChain ( p . ethereum . BlockChain ( ) . CurrentBlock . Hash ( ) , p . blocksRequested )
2014-03-21 09:06:23 -05:00
2014-05-20 04:50:34 -05:00
var hashes [ ] interface { }
for _ , block := range blocks {
hashes = append ( hashes , block . Hash ( ) )
2014-03-21 09:06:23 -05:00
}
2014-01-30 17:56:32 -06:00
2014-05-20 04:50:34 -05:00
msgInfo := append ( hashes , uint64 ( len ( hashes ) ) )
2014-06-25 10:40:06 -05:00
peerlogger . DebugDetailf ( "Asking for block from %x (%d total) from %s\n" , p . ethereum . BlockChain ( ) . CurrentBlock . Hash ( ) , len ( hashes ) , p . conn . RemoteAddr ( ) . String ( ) )
2014-05-20 04:50:34 -05:00
msg := ethwire . NewMessage ( ethwire . MsgGetChainTy , msgInfo )
p . QueueMessage ( msg )
}
2014-03-21 09:06:23 -05:00
func ( p * Peer ) CatchupWithPeer ( blockHash [ ] byte ) {
2014-02-09 18:09:12 -06:00
if ! p . catchingUp {
2014-05-20 04:50:34 -05:00
// Make sure nobody else is catching up when you want to do this
2014-02-09 18:09:12 -06:00
p . catchingUp = true
2014-07-29 16:34:21 -05:00
msg := ethwire . NewMessage ( ethwire . MsgGetChainTy , [ ] interface { } { blockHash , uint64 ( 100 ) } )
2014-02-09 18:09:12 -06:00
p . QueueMessage ( msg )
2014-06-25 10:40:06 -05:00
peerlogger . DebugDetailf ( "Requesting blockchain %x... from peer %s\n" , p . ethereum . BlockChain ( ) . CurrentBlock . Hash ( ) [ : 4 ] , p . conn . RemoteAddr ( ) )
2014-03-28 05:20:07 -05:00
2014-05-21 05:39:15 -05:00
msg = ethwire . NewMessage ( ethwire . MsgGetTxsTy , [ ] interface { } { } )
p . QueueMessage ( msg )
2014-02-09 18:09:12 -06:00
}
}
2014-01-31 13:01:28 -06:00
func ( p * Peer ) RlpData ( ) [ ] interface { } {
2014-02-09 16:34:33 -06:00
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-05-22 10:33:46 -05:00
func packAddr ( address , port string ) ( [ ] byte , uint16 ) {
2014-05-01 15:13:59 -05:00
addr := strings . Split ( address , "." )
a , _ := strconv . Atoi ( addr [ 0 ] )
b , _ := strconv . Atoi ( addr [ 1 ] )
c , _ := strconv . Atoi ( addr [ 2 ] )
d , _ := strconv . Atoi ( addr [ 3 ] )
2014-05-22 10:33:46 -05:00
host := [ ] byte { byte ( a ) , byte ( b ) , byte ( c ) , byte ( d ) }
2014-05-01 15:13:59 -05:00
prt , _ := strconv . Atoi ( port )
return host , uint16 ( prt )
}
func unpackAddr ( value * ethutil . Value , p uint64 ) string {
2014-05-22 10:33:46 -05:00
byts := value . Bytes ( )
a := strconv . Itoa ( int ( byts [ 0 ] ) )
b := strconv . Itoa ( int ( byts [ 1 ] ) )
c := strconv . Itoa ( int ( byts [ 2 ] ) )
d := strconv . Itoa ( int ( byts [ 3 ] ) )
2014-05-01 15:13:59 -05:00
host := strings . Join ( [ ] string { a , b , c , d } , "." )
port := strconv . Itoa ( int ( p ) )
return net . JoinHostPort ( host , port )
}