1// Copyright 2015 The go-ethereum Authors 2// This file is part of the go-ethereum library. 3// 4// The go-ethereum library is free software: you can redistribute it and/or modify 5// it under the terms of the GNU Lesser General Public License as published by 6// the Free Software Foundation, either version 3 of the License, or 7// (at your option) any later version. 8// 9// The go-ethereum library is distributed in the hope that it will be useful, 10// but WITHOUT ANY WARRANTY; without even the implied warranty of 11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12// GNU Lesser General Public License for more details. 13// 14// You should have received a copy of the GNU Lesser General Public License 15// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17package eth 18 19import ( 20 "fmt" 21 "math/big" 22 "time" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/forkid" 26 "github.com/ethereum/go-ethereum/p2p" 27) 28 29const ( 30 // handshakeTimeout is the maximum allowed time for the `eth` handshake to 31 // complete before dropping the connection.= as malicious. 32 handshakeTimeout = 5 * time.Second 33) 34 35// Handshake executes the eth protocol handshake, negotiating version number, 36// network IDs, difficulties, head and genesis blocks. 37func (p *Peer) Handshake(network uint64, td *big.Int, head common.Hash, genesis common.Hash, forkID forkid.ID, forkFilter forkid.Filter) error { 38 // Send out own handshake in a new thread 39 errc := make(chan error, 2) 40 41 var status StatusPacket // safe to read after two values have been received from errc 42 43 go func() { 44 errc <- p2p.Send(p.rw, StatusMsg, &StatusPacket{ 45 ProtocolVersion: uint32(p.version), 46 NetworkID: network, 47 TD: td, 48 Head: head, 49 Genesis: genesis, 50 ForkID: forkID, 51 }) 52 }() 53 go func() { 54 errc <- p.readStatus(network, &status, genesis, forkFilter) 55 }() 56 timeout := time.NewTimer(handshakeTimeout) 57 defer timeout.Stop() 58 for i := 0; i < 2; i++ { 59 select { 60 case err := <-errc: 61 if err != nil { 62 return err 63 } 64 case <-timeout.C: 65 return p2p.DiscReadTimeout 66 } 67 } 68 p.td, p.head = status.TD, status.Head 69 70 // TD at mainnet block #7753254 is 76 bits. If it becomes 100 million times 71 // larger, it will still fit within 100 bits 72 if tdlen := p.td.BitLen(); tdlen > 100 { 73 return fmt.Errorf("too large total difficulty: bitlen %d", tdlen) 74 } 75 return nil 76} 77 78// readStatus reads the remote handshake message. 79func (p *Peer) readStatus(network uint64, status *StatusPacket, genesis common.Hash, forkFilter forkid.Filter) error { 80 msg, err := p.rw.ReadMsg() 81 if err != nil { 82 return err 83 } 84 if msg.Code != StatusMsg { 85 return fmt.Errorf("%w: first msg has code %x (!= %x)", errNoStatusMsg, msg.Code, StatusMsg) 86 } 87 if msg.Size > maxMessageSize { 88 return fmt.Errorf("%w: %v > %v", errMsgTooLarge, msg.Size, maxMessageSize) 89 } 90 // Decode the handshake and make sure everything matches 91 if err := msg.Decode(&status); err != nil { 92 return fmt.Errorf("%w: message %v: %v", errDecode, msg, err) 93 } 94 if status.NetworkID != network { 95 return fmt.Errorf("%w: %d (!= %d)", errNetworkIDMismatch, status.NetworkID, network) 96 } 97 if uint(status.ProtocolVersion) != p.version { 98 return fmt.Errorf("%w: %d (!= %d)", errProtocolVersionMismatch, status.ProtocolVersion, p.version) 99 } 100 if status.Genesis != genesis { 101 return fmt.Errorf("%w: %x (!= %x)", errGenesisMismatch, status.Genesis, genesis) 102 } 103 if err := forkFilter(status.ForkID); err != nil { 104 return fmt.Errorf("%w: %v", errForkIDRejected, err) 105 } 106 return nil 107} 108