OSDN Git Service

Merge pull request #935 from Bytom/dev
[bytom/bytom.git] / p2p / peer.go
1 package p2p
2
3 import (
4         "fmt"
5         "io"
6         "net"
7         "time"
8
9         "github.com/pkg/errors"
10         log "github.com/sirupsen/logrus"
11         crypto "github.com/tendermint/go-crypto"
12         wire "github.com/tendermint/go-wire"
13         cmn "github.com/tendermint/tmlibs/common"
14
15         cfg "github.com/bytom/config"
16 )
17
18 // Peer could be marked as persistent, in which case you can use
19 // Redial function to reconnect. Note that inbound peers can't be
20 // made persistent. They should be made persistent on the other end.
21 //
22 // Before using a peer, you will need to perform a handshake on connection.
23 type Peer struct {
24         cmn.BaseService
25
26         outbound bool
27
28         conn  net.Conn     // source connection
29         mconn *MConnection // multiplex connection
30
31         persistent bool
32         config     *PeerConfig
33
34         *NodeInfo
35         Key  string
36         Data *cmn.CMap // User data.
37 }
38
39 // PeerConfig is a Peer configuration.
40 type PeerConfig struct {
41         AuthEnc bool `mapstructure:"auth_enc"` // authenticated encryption
42
43         // times are in seconds
44         HandshakeTimeout time.Duration `mapstructure:"handshake_timeout"`
45         DialTimeout      time.Duration `mapstructure:"dial_timeout"`
46
47         MConfig *MConnConfig `mapstructure:"connection"`
48
49         Fuzz       bool            `mapstructure:"fuzz"` // fuzz connection (for testing)
50         FuzzConfig *FuzzConnConfig `mapstructure:"fuzz_config"`
51 }
52
53 // DefaultPeerConfig returns the default config.
54 func DefaultPeerConfig(config *cfg.P2PConfig) *PeerConfig {
55         return &PeerConfig{
56                 AuthEnc:          true,
57                 HandshakeTimeout: time.Duration(config.HandshakeTimeout), // * time.Second,
58                 DialTimeout:      time.Duration(config.DialTimeout),  // * time.Second,
59                 MConfig:          DefaultMConnConfig(),
60                 Fuzz:             false,
61                 FuzzConfig:       DefaultFuzzConnConfig(),
62         }
63 }
64
65 func newOutboundPeer(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*Peer, error) {
66         return newOutboundPeerWithConfig(addr, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig(config))
67 }
68
69 func newOutboundPeerWithConfig(addr *NetAddress, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
70         conn, err := dial(addr, config)
71         if err != nil {
72                 return nil, errors.Wrap(err, "Error creating peer")
73         }
74
75         peer, err := newPeerFromConnAndConfig(conn, true, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
76         if err != nil {
77                 conn.Close()
78                 return nil, err
79         }
80         return peer, nil
81 }
82
83 func newInboundPeer(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *cfg.P2PConfig) (*Peer, error) {
84         return newInboundPeerWithConfig(conn, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, DefaultPeerConfig(config))
85 }
86
87 func newInboundPeerWithConfig(conn net.Conn, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
88         return newPeerFromConnAndConfig(conn, false, reactorsByCh, chDescs, onPeerError, ourNodePrivKey, config)
89 }
90
91 func newPeerFromConnAndConfig(rawConn net.Conn, outbound bool, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), ourNodePrivKey crypto.PrivKeyEd25519, config *PeerConfig) (*Peer, error) {
92         conn := rawConn
93
94         // Fuzz connection
95         if config.Fuzz {
96                 // so we have time to do peer handshakes and get set up
97                 conn = FuzzConnAfterFromConfig(conn, 10*time.Second, config.FuzzConfig)
98         }
99
100         // Encrypt connection
101         if config.AuthEnc {
102                 conn.SetDeadline(time.Now().Add(config.HandshakeTimeout * time.Second))
103
104                 var err error
105                 conn, err = MakeSecretConnection(conn, ourNodePrivKey)
106                 if err != nil {
107                         return nil, errors.Wrap(err, "Error creating peer")
108                 }
109         }
110
111         // Key and NodeInfo are set after Handshake
112         p := &Peer{
113                 outbound: outbound,
114                 conn:     conn,
115                 config:   config,
116                 Data:     cmn.NewCMap(),
117         }
118
119         p.mconn = createMConnection(conn, p, reactorsByCh, chDescs, onPeerError, config.MConfig)
120
121         p.BaseService = *cmn.NewBaseService(nil, "Peer", p)
122
123         return p, nil
124 }
125
126 // CloseConn should be used when the peer was created, but never started.
127 func (p *Peer) CloseConn() {
128         p.conn.Close()
129 }
130
131 // makePersistent marks the peer as persistent.
132 func (p *Peer) makePersistent() {
133         if !p.outbound {
134                 panic("inbound peers can't be made persistent")
135         }
136
137         p.persistent = true
138 }
139
140 // IsPersistent returns true if the peer is persitent, false otherwise.
141 func (p *Peer) IsPersistent() bool {
142         return p.persistent
143 }
144
145 // HandshakeTimeout performs a handshake between a given node and the peer.
146 // NOTE: blocking
147 func (p *Peer) HandshakeTimeout(ourNodeInfo *NodeInfo, timeout time.Duration) error {
148         // Set deadline for handshake so we don't block forever on conn.ReadFull
149         p.conn.SetDeadline(time.Now().Add(timeout))
150
151         var peerNodeInfo = new(NodeInfo)
152         var err1 error
153         var err2 error
154         cmn.Parallel(
155                 func() {
156                         var n int
157                         wire.WriteBinary(ourNodeInfo, p.conn, &n, &err1)
158                 },
159                 func() {
160                         var n int
161                         wire.ReadBinary(peerNodeInfo, p.conn, maxNodeInfoSize, &n, &err2)
162                         log.WithField("peerNodeInfo", peerNodeInfo).Info("Peer handshake")
163                 })
164         if err1 != nil {
165                 return errors.Wrap(err1, "Error during handshake/write")
166         }
167         if err2 != nil {
168                 return errors.Wrap(err2, "Error during handshake/read")
169         }
170
171         if p.config.AuthEnc {
172                 // Check that the professed PubKey matches the sconn's.
173                 if !peerNodeInfo.PubKey.Equals(p.PubKey().Wrap()) {
174                         return fmt.Errorf("Ignoring connection with unmatching pubkey: %v vs %v",
175                                 peerNodeInfo.PubKey, p.PubKey())
176                 }
177         }
178
179         // Remove deadline
180         p.conn.SetDeadline(time.Time{})
181
182         peerNodeInfo.RemoteAddr = p.Addr().String()
183
184         p.NodeInfo = peerNodeInfo
185         p.Key = peerNodeInfo.PubKey.KeyString()
186
187         return nil
188 }
189
190 // Addr returns peer's remote network address.
191 func (p *Peer) Addr() net.Addr {
192         return p.conn.RemoteAddr()
193 }
194
195 // PubKey returns peer's public key.
196 func (p *Peer) PubKey() crypto.PubKeyEd25519 {
197         if p.config.AuthEnc {
198                 return p.conn.(*SecretConnection).RemotePubKey()
199         }
200         if p.NodeInfo == nil {
201                 panic("Attempt to get peer's PubKey before calling Handshake")
202         }
203         return p.PubKey()
204 }
205
206 // OnStart implements BaseService.
207 func (p *Peer) OnStart() error {
208         p.BaseService.OnStart()
209         _, err := p.mconn.Start()
210         return err
211 }
212
213 // OnStop implements BaseService.
214 func (p *Peer) OnStop() {
215         p.BaseService.OnStop()
216         p.mconn.Stop()
217 }
218
219 // Connection returns underlying MConnection.
220 func (p *Peer) Connection() *MConnection {
221         return p.mconn
222 }
223
224 // IsOutbound returns true if the connection is outbound, false otherwise.
225 func (p *Peer) IsOutbound() bool {
226         return p.outbound
227 }
228
229 // Send msg to the channel identified by chID byte. Returns false if the send
230 // queue is full after timeout, specified by MConnection.
231 func (p *Peer) Send(chID byte, msg interface{}) bool {
232         if !p.IsRunning() {
233                 // see Switch#Broadcast, where we fetch the list of peers and loop over
234                 // them - while we're looping, one peer may be removed and stopped.
235                 return false
236         }
237         return p.mconn.Send(chID, msg)
238 }
239
240 // TrySend msg to the channel identified by chID byte. Immediately returns
241 // false if the send queue is full.
242 func (p *Peer) TrySend(chID byte, msg interface{}) bool {
243         if !p.IsRunning() {
244                 return false
245         }
246         return p.mconn.TrySend(chID, msg)
247 }
248
249 // CanSend returns true if the send queue is not full, false otherwise.
250 func (p *Peer) CanSend(chID byte) bool {
251         if !p.IsRunning() {
252                 return false
253         }
254         return p.mconn.CanSend(chID)
255 }
256
257 // WriteTo writes the peer's public key to w.
258 func (p *Peer) WriteTo(w io.Writer) (n int64, err error) {
259         var n_ int
260         wire.WriteString(p.Key, w, &n_, &err)
261         n += int64(n_)
262         return
263 }
264
265 // String representation.
266 func (p *Peer) String() string {
267         if p.outbound {
268                 return fmt.Sprintf("Peer{%v %v out}", p.mconn, p.Key[:12])
269         }
270
271         return fmt.Sprintf("Peer{%v %v in}", p.mconn, p.Key[:12])
272 }
273
274 // Equals reports whenever 2 peers are actually represent the same node.
275 func (p *Peer) Equals(other *Peer) bool {
276         return p.Key == other.Key
277 }
278
279 // Get the data for a given key.
280 func (p *Peer) Get(key string) interface{} {
281         return p.Data.Get(key)
282 }
283
284 func dial(addr *NetAddress, config *PeerConfig) (net.Conn, error) {
285         conn, err := addr.DialTimeout(config.DialTimeout * time.Second)
286         if err != nil {
287                 return nil, err
288         }
289         return conn, nil
290 }
291
292 func createMConnection(conn net.Conn, p *Peer, reactorsByCh map[byte]Reactor, chDescs []*ChannelDescriptor, onPeerError func(*Peer, interface{}), config *MConnConfig) *MConnection {
293         onReceive := func(chID byte, msgBytes []byte) {
294                 reactor := reactorsByCh[chID]
295                 if reactor == nil {
296                         if chID == PexChannel {
297                                 return
298                         } else {
299                                 cmn.PanicSanity(cmn.Fmt("Unknown channel %X", chID))
300                         }
301                 }
302                 reactor.Receive(chID, p, msgBytes)
303         }
304
305         onError := func(r interface{}) {
306                 onPeerError(p, r)
307         }
308
309         return NewMConnectionWithConfig(conn, chDescs, onReceive, onError, config)
310 }