github.com/daragao/go-ethereum@v1.8.14-0.20180809141559-45eaef243198/swarm/network/protocol.go (about) 1 // Copyright 2016 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 17 package network 18 19 import ( 20 "context" 21 "errors" 22 "fmt" 23 "net" 24 "sync" 25 "time" 26 27 "github.com/ethereum/go-ethereum/crypto" 28 "github.com/ethereum/go-ethereum/p2p" 29 "github.com/ethereum/go-ethereum/p2p/discover" 30 "github.com/ethereum/go-ethereum/p2p/protocols" 31 "github.com/ethereum/go-ethereum/rpc" 32 "github.com/ethereum/go-ethereum/swarm/log" 33 "github.com/ethereum/go-ethereum/swarm/state" 34 ) 35 36 const ( 37 DefaultNetworkID = 3 38 // ProtocolMaxMsgSize maximum allowed message size 39 ProtocolMaxMsgSize = 10 * 1024 * 1024 40 // timeout for waiting 41 bzzHandshakeTimeout = 3000 * time.Millisecond 42 ) 43 44 // BzzSpec is the spec of the generic swarm handshake 45 var BzzSpec = &protocols.Spec{ 46 Name: "bzz", 47 Version: 5, 48 MaxMsgSize: 10 * 1024 * 1024, 49 Messages: []interface{}{ 50 HandshakeMsg{}, 51 }, 52 } 53 54 // DiscoverySpec is the spec for the bzz discovery subprotocols 55 var DiscoverySpec = &protocols.Spec{ 56 Name: "hive", 57 Version: 5, 58 MaxMsgSize: 10 * 1024 * 1024, 59 Messages: []interface{}{ 60 peersMsg{}, 61 subPeersMsg{}, 62 }, 63 } 64 65 // Addr interface that peerPool needs 66 type Addr interface { 67 OverlayPeer 68 Over() []byte 69 Under() []byte 70 String() string 71 Update(OverlayAddr) OverlayAddr 72 } 73 74 // Peer interface represents an live peer connection 75 type Peer interface { 76 Addr // the address of a peer 77 Conn // the live connection (protocols.Peer) 78 LastActive() time.Time // last time active 79 } 80 81 // Conn interface represents an live peer connection 82 type Conn interface { 83 ID() discover.NodeID // the key that uniquely identifies the Node for the peerPool 84 Handshake(context.Context, interface{}, func(interface{}) error) (interface{}, error) // can send messages 85 Send(context.Context, interface{}) error // can send messages 86 Drop(error) // disconnect this peer 87 Run(func(context.Context, interface{}) error) error // the run function to run a protocol 88 Off() OverlayAddr 89 } 90 91 // BzzConfig captures the config params used by the hive 92 type BzzConfig struct { 93 OverlayAddr []byte // base address of the overlay network 94 UnderlayAddr []byte // node's underlay address 95 HiveParams *HiveParams 96 NetworkID uint64 97 LightNode bool 98 } 99 100 // Bzz is the swarm protocol bundle 101 type Bzz struct { 102 *Hive 103 NetworkID uint64 104 LightNode bool 105 localAddr *BzzAddr 106 mtx sync.Mutex 107 handshakes map[discover.NodeID]*HandshakeMsg 108 streamerSpec *protocols.Spec 109 streamerRun func(*BzzPeer) error 110 } 111 112 // NewBzz is the swarm protocol constructor 113 // arguments 114 // * bzz config 115 // * overlay driver 116 // * peer store 117 func NewBzz(config *BzzConfig, kad Overlay, store state.Store, streamerSpec *protocols.Spec, streamerRun func(*BzzPeer) error) *Bzz { 118 return &Bzz{ 119 Hive: NewHive(config.HiveParams, kad, store), 120 NetworkID: config.NetworkID, 121 LightNode: config.LightNode, 122 localAddr: &BzzAddr{config.OverlayAddr, config.UnderlayAddr}, 123 handshakes: make(map[discover.NodeID]*HandshakeMsg), 124 streamerRun: streamerRun, 125 streamerSpec: streamerSpec, 126 } 127 } 128 129 // UpdateLocalAddr updates underlayaddress of the running node 130 func (b *Bzz) UpdateLocalAddr(byteaddr []byte) *BzzAddr { 131 b.localAddr = b.localAddr.Update(&BzzAddr{ 132 UAddr: byteaddr, 133 OAddr: b.localAddr.OAddr, 134 }).(*BzzAddr) 135 return b.localAddr 136 } 137 138 // NodeInfo returns the node's overlay address 139 func (b *Bzz) NodeInfo() interface{} { 140 return b.localAddr.Address() 141 } 142 143 // Protocols return the protocols swarm offers 144 // Bzz implements the node.Service interface 145 // * handshake/hive 146 // * discovery 147 func (b *Bzz) Protocols() []p2p.Protocol { 148 protocol := []p2p.Protocol{ 149 { 150 Name: BzzSpec.Name, 151 Version: BzzSpec.Version, 152 Length: BzzSpec.Length(), 153 Run: b.runBzz, 154 NodeInfo: b.NodeInfo, 155 }, 156 { 157 Name: DiscoverySpec.Name, 158 Version: DiscoverySpec.Version, 159 Length: DiscoverySpec.Length(), 160 Run: b.RunProtocol(DiscoverySpec, b.Hive.Run), 161 NodeInfo: b.Hive.NodeInfo, 162 PeerInfo: b.Hive.PeerInfo, 163 }, 164 } 165 if b.streamerSpec != nil && b.streamerRun != nil { 166 protocol = append(protocol, p2p.Protocol{ 167 Name: b.streamerSpec.Name, 168 Version: b.streamerSpec.Version, 169 Length: b.streamerSpec.Length(), 170 Run: b.RunProtocol(b.streamerSpec, b.streamerRun), 171 }) 172 } 173 return protocol 174 } 175 176 // APIs returns the APIs offered by bzz 177 // * hive 178 // Bzz implements the node.Service interface 179 func (b *Bzz) APIs() []rpc.API { 180 return []rpc.API{{ 181 Namespace: "hive", 182 Version: "3.0", 183 Service: b.Hive, 184 }} 185 } 186 187 // RunProtocol is a wrapper for swarm subprotocols 188 // returns a p2p protocol run function that can be assigned to p2p.Protocol#Run field 189 // arguments: 190 // * p2p protocol spec 191 // * run function taking BzzPeer as argument 192 // this run function is meant to block for the duration of the protocol session 193 // on return the session is terminated and the peer is disconnected 194 // the protocol waits for the bzz handshake is negotiated 195 // the overlay address on the BzzPeer is set from the remote handshake 196 func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error { 197 return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 198 // wait for the bzz protocol to perform the handshake 199 handshake, _ := b.GetHandshake(p.ID()) 200 defer b.removeHandshake(p.ID()) 201 select { 202 case <-handshake.done: 203 case <-time.After(bzzHandshakeTimeout): 204 return fmt.Errorf("%08x: %s protocol timeout waiting for handshake on %08x", b.BaseAddr()[:4], spec.Name, p.ID().Bytes()[:4]) 205 } 206 if handshake.err != nil { 207 return fmt.Errorf("%08x: %s protocol closed: %v", b.BaseAddr()[:4], spec.Name, handshake.err) 208 } 209 // the handshake has succeeded so construct the BzzPeer and run the protocol 210 peer := &BzzPeer{ 211 Peer: protocols.NewPeer(p, rw, spec), 212 localAddr: b.localAddr, 213 BzzAddr: handshake.peerAddr, 214 lastActive: time.Now(), 215 LightNode: handshake.LightNode, 216 } 217 218 log.Debug("peer created", "addr", handshake.peerAddr.String()) 219 220 return run(peer) 221 } 222 } 223 224 // performHandshake implements the negotiation of the bzz handshake 225 // shared among swarm subprotocols 226 func (b *Bzz) performHandshake(p *protocols.Peer, handshake *HandshakeMsg) error { 227 ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout) 228 defer func() { 229 close(handshake.done) 230 cancel() 231 }() 232 rsh, err := p.Handshake(ctx, handshake, b.checkHandshake) 233 if err != nil { 234 handshake.err = err 235 return err 236 } 237 handshake.peerAddr = rsh.(*HandshakeMsg).Addr 238 handshake.LightNode = rsh.(*HandshakeMsg).LightNode 239 return nil 240 } 241 242 // runBzz is the p2p protocol run function for the bzz base protocol 243 // that negotiates the bzz handshake 244 func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error { 245 handshake, _ := b.GetHandshake(p.ID()) 246 if !<-handshake.init { 247 return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4]) 248 } 249 close(handshake.init) 250 defer b.removeHandshake(p.ID()) 251 peer := protocols.NewPeer(p, rw, BzzSpec) 252 err := b.performHandshake(peer, handshake) 253 if err != nil { 254 log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], ToOverlayAddr(p.ID().Bytes())[:4], err)) 255 256 return err 257 } 258 // fail if we get another handshake 259 msg, err := rw.ReadMsg() 260 if err != nil { 261 return err 262 } 263 msg.Discard() 264 return errors.New("received multiple handshakes") 265 } 266 267 // BzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) 268 // implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer 269 type BzzPeer struct { 270 *protocols.Peer // represents the connection for online peers 271 localAddr *BzzAddr // local Peers address 272 *BzzAddr // remote address -> implements Addr interface = protocols.Peer 273 lastActive time.Time // time is updated whenever mutexes are releasing 274 LightNode bool 275 } 276 277 func NewBzzTestPeer(p *protocols.Peer, addr *BzzAddr) *BzzPeer { 278 return &BzzPeer{ 279 Peer: p, 280 localAddr: addr, 281 BzzAddr: NewAddrFromNodeID(p.ID()), 282 } 283 } 284 285 // Off returns the overlay peer record for offline persistence 286 func (p *BzzPeer) Off() OverlayAddr { 287 return p.BzzAddr 288 } 289 290 // LastActive returns the time the peer was last active 291 func (p *BzzPeer) LastActive() time.Time { 292 return p.lastActive 293 } 294 295 /* 296 Handshake 297 298 * Version: 8 byte integer version of the protocol 299 * NetworkID: 8 byte integer network identifier 300 * Addr: the address advertised by the node including underlay and overlay connecctions 301 */ 302 type HandshakeMsg struct { 303 Version uint64 304 NetworkID uint64 305 Addr *BzzAddr 306 LightNode bool 307 308 // peerAddr is the address received in the peer handshake 309 peerAddr *BzzAddr 310 311 init chan bool 312 done chan struct{} 313 err error 314 } 315 316 // String pretty prints the handshake 317 func (bh *HandshakeMsg) String() string { 318 return fmt.Sprintf("Handshake: Version: %v, NetworkID: %v, Addr: %v, LightNode: %v, peerAddr: %v", bh.Version, bh.NetworkID, bh.Addr, bh.LightNode, bh.peerAddr) 319 } 320 321 // Perform initiates the handshake and validates the remote handshake message 322 func (b *Bzz) checkHandshake(hs interface{}) error { 323 rhs := hs.(*HandshakeMsg) 324 if rhs.NetworkID != b.NetworkID { 325 return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkID, b.NetworkID) 326 } 327 if rhs.Version != uint64(BzzSpec.Version) { 328 return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, BzzSpec.Version) 329 } 330 return nil 331 } 332 333 // removeHandshake removes handshake for peer with peerID 334 // from the bzz handshake store 335 func (b *Bzz) removeHandshake(peerID discover.NodeID) { 336 b.mtx.Lock() 337 defer b.mtx.Unlock() 338 delete(b.handshakes, peerID) 339 } 340 341 // GetHandshake returns the bzz handhake that the remote peer with peerID sent 342 func (b *Bzz) GetHandshake(peerID discover.NodeID) (*HandshakeMsg, bool) { 343 b.mtx.Lock() 344 defer b.mtx.Unlock() 345 handshake, found := b.handshakes[peerID] 346 if !found { 347 handshake = &HandshakeMsg{ 348 Version: uint64(BzzSpec.Version), 349 NetworkID: b.NetworkID, 350 Addr: b.localAddr, 351 LightNode: b.LightNode, 352 init: make(chan bool, 1), 353 done: make(chan struct{}), 354 } 355 // when handhsake is first created for a remote peer 356 // it is initialised with the init 357 handshake.init <- true 358 b.handshakes[peerID] = handshake 359 } 360 361 return handshake, found 362 } 363 364 // BzzAddr implements the PeerAddr interface 365 type BzzAddr struct { 366 OAddr []byte 367 UAddr []byte 368 } 369 370 // Address implements OverlayPeer interface to be used in Overlay 371 func (a *BzzAddr) Address() []byte { 372 return a.OAddr 373 } 374 375 // Over returns the overlay address 376 func (a *BzzAddr) Over() []byte { 377 return a.OAddr 378 } 379 380 // Under returns the underlay address 381 func (a *BzzAddr) Under() []byte { 382 return a.UAddr 383 } 384 385 // ID returns the nodeID from the underlay enode address 386 func (a *BzzAddr) ID() discover.NodeID { 387 return discover.MustParseNode(string(a.UAddr)).ID 388 } 389 390 // Update updates the underlay address of a peer record 391 func (a *BzzAddr) Update(na OverlayAddr) OverlayAddr { 392 return &BzzAddr{a.OAddr, na.(Addr).Under()} 393 } 394 395 // String pretty prints the address 396 func (a *BzzAddr) String() string { 397 return fmt.Sprintf("%x <%s>", a.OAddr, a.UAddr) 398 } 399 400 // RandomAddr is a utility method generating an address from a public key 401 func RandomAddr() *BzzAddr { 402 key, err := crypto.GenerateKey() 403 if err != nil { 404 panic("unable to generate key") 405 } 406 pubkey := crypto.FromECDSAPub(&key.PublicKey) 407 var id discover.NodeID 408 copy(id[:], pubkey[1:]) 409 return NewAddrFromNodeID(id) 410 } 411 412 // NewNodeIDFromAddr transforms the underlay address to an adapters.NodeID 413 func NewNodeIDFromAddr(addr Addr) discover.NodeID { 414 log.Info(fmt.Sprintf("uaddr=%s", string(addr.Under()))) 415 node := discover.MustParseNode(string(addr.Under())) 416 return node.ID 417 } 418 419 // NewAddrFromNodeID constucts a BzzAddr from a discover.NodeID 420 // the overlay address is derived as the hash of the nodeID 421 func NewAddrFromNodeID(id discover.NodeID) *BzzAddr { 422 return &BzzAddr{ 423 OAddr: ToOverlayAddr(id.Bytes()), 424 UAddr: []byte(discover.NewNode(id, net.IP{127, 0, 0, 1}, 30303, 30303).String()), 425 } 426 } 427 428 // NewAddrFromNodeIDAndPort constucts a BzzAddr from a discover.NodeID and port uint16 429 // the overlay address is derived as the hash of the nodeID 430 func NewAddrFromNodeIDAndPort(id discover.NodeID, host net.IP, port uint16) *BzzAddr { 431 return &BzzAddr{ 432 OAddr: ToOverlayAddr(id.Bytes()), 433 UAddr: []byte(discover.NewNode(id, host, port, port).String()), 434 } 435 } 436 437 // ToOverlayAddr creates an overlayaddress from a byte slice 438 func ToOverlayAddr(id []byte) []byte { 439 return crypto.Keccak256(id) 440 }