github.com/daeglee/go-ethereum@v0.0.0-20190504220456-cad3e8d18e9b/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 "sync" 24 "time" 25 26 "github.com/ethereum/go-ethereum/p2p" 27 "github.com/ethereum/go-ethereum/p2p/enode" 28 "github.com/ethereum/go-ethereum/p2p/protocols" 29 "github.com/ethereum/go-ethereum/rpc" 30 "github.com/ethereum/go-ethereum/swarm/log" 31 "github.com/ethereum/go-ethereum/swarm/state" 32 ) 33 34 const ( 35 DefaultNetworkID = 3 36 // timeout for waiting 37 bzzHandshakeTimeout = 3000 * time.Millisecond 38 ) 39 40 // BzzSpec is the spec of the generic swarm handshake 41 var BzzSpec = &protocols.Spec{ 42 Name: "bzz", 43 Version: 8, 44 MaxMsgSize: 10 * 1024 * 1024, 45 Messages: []interface{}{ 46 HandshakeMsg{}, 47 }, 48 } 49 50 // DiscoverySpec is the spec for the bzz discovery subprotocols 51 var DiscoverySpec = &protocols.Spec{ 52 Name: "hive", 53 Version: 8, 54 MaxMsgSize: 10 * 1024 * 1024, 55 Messages: []interface{}{ 56 peersMsg{}, 57 subPeersMsg{}, 58 }, 59 } 60 61 // BzzConfig captures the config params used by the hive 62 type BzzConfig struct { 63 OverlayAddr []byte // base address of the overlay network 64 UnderlayAddr []byte // node's underlay address 65 HiveParams *HiveParams 66 NetworkID uint64 67 LightNode bool 68 BootnodeMode bool 69 } 70 71 // Bzz is the swarm protocol bundle 72 type Bzz struct { 73 *Hive 74 NetworkID uint64 75 LightNode bool 76 localAddr *BzzAddr 77 mtx sync.Mutex 78 handshakes map[enode.ID]*HandshakeMsg 79 streamerSpec *protocols.Spec 80 streamerRun func(*BzzPeer) error 81 } 82 83 // NewBzz is the swarm protocol constructor 84 // arguments 85 // * bzz config 86 // * overlay driver 87 // * peer store 88 func NewBzz(config *BzzConfig, kad *Kademlia, store state.Store, streamerSpec *protocols.Spec, streamerRun func(*BzzPeer) error) *Bzz { 89 bzz := &Bzz{ 90 Hive: NewHive(config.HiveParams, kad, store), 91 NetworkID: config.NetworkID, 92 LightNode: config.LightNode, 93 localAddr: &BzzAddr{config.OverlayAddr, config.UnderlayAddr}, 94 handshakes: make(map[enode.ID]*HandshakeMsg), 95 streamerRun: streamerRun, 96 streamerSpec: streamerSpec, 97 } 98 99 if config.BootnodeMode { 100 bzz.streamerRun = nil 101 bzz.streamerSpec = nil 102 } 103 104 return bzz 105 } 106 107 // UpdateLocalAddr updates underlayaddress of the running node 108 func (b *Bzz) UpdateLocalAddr(byteaddr []byte) *BzzAddr { 109 b.localAddr = b.localAddr.Update(&BzzAddr{ 110 UAddr: byteaddr, 111 OAddr: b.localAddr.OAddr, 112 }) 113 return b.localAddr 114 } 115 116 // NodeInfo returns the node's overlay address 117 func (b *Bzz) NodeInfo() interface{} { 118 return b.localAddr.Address() 119 } 120 121 // Protocols return the protocols swarm offers 122 // Bzz implements the node.Service interface 123 // * handshake/hive 124 // * discovery 125 func (b *Bzz) Protocols() []p2p.Protocol { 126 protocol := []p2p.Protocol{ 127 { 128 Name: BzzSpec.Name, 129 Version: BzzSpec.Version, 130 Length: BzzSpec.Length(), 131 Run: b.runBzz, 132 NodeInfo: b.NodeInfo, 133 }, 134 { 135 Name: DiscoverySpec.Name, 136 Version: DiscoverySpec.Version, 137 Length: DiscoverySpec.Length(), 138 Run: b.RunProtocol(DiscoverySpec, b.Hive.Run), 139 NodeInfo: b.Hive.NodeInfo, 140 PeerInfo: b.Hive.PeerInfo, 141 }, 142 } 143 if b.streamerSpec != nil && b.streamerRun != nil { 144 protocol = append(protocol, p2p.Protocol{ 145 Name: b.streamerSpec.Name, 146 Version: b.streamerSpec.Version, 147 Length: b.streamerSpec.Length(), 148 Run: b.RunProtocol(b.streamerSpec, b.streamerRun), 149 }) 150 } 151 return protocol 152 } 153 154 // APIs returns the APIs offered by bzz 155 // * hive 156 // Bzz implements the node.Service interface 157 func (b *Bzz) APIs() []rpc.API { 158 return []rpc.API{{ 159 Namespace: "hive", 160 Version: "3.0", 161 Service: b.Hive, 162 }} 163 } 164 165 // RunProtocol is a wrapper for swarm subprotocols 166 // returns a p2p protocol run function that can be assigned to p2p.Protocol#Run field 167 // arguments: 168 // * p2p protocol spec 169 // * run function taking BzzPeer as argument 170 // this run function is meant to block for the duration of the protocol session 171 // on return the session is terminated and the peer is disconnected 172 // the protocol waits for the bzz handshake is negotiated 173 // the overlay address on the BzzPeer is set from the remote handshake 174 func (b *Bzz) RunProtocol(spec *protocols.Spec, run func(*BzzPeer) error) func(*p2p.Peer, p2p.MsgReadWriter) error { 175 return func(p *p2p.Peer, rw p2p.MsgReadWriter) error { 176 // wait for the bzz protocol to perform the handshake 177 handshake, _ := b.GetOrCreateHandshake(p.ID()) 178 defer b.removeHandshake(p.ID()) 179 select { 180 case <-handshake.done: 181 case <-time.After(bzzHandshakeTimeout): 182 return fmt.Errorf("%08x: %s protocol timeout waiting for handshake on %08x", b.BaseAddr()[:4], spec.Name, p.ID().Bytes()[:4]) 183 } 184 if handshake.err != nil { 185 return fmt.Errorf("%08x: %s protocol closed: %v", b.BaseAddr()[:4], spec.Name, handshake.err) 186 } 187 // the handshake has succeeded so construct the BzzPeer and run the protocol 188 peer := &BzzPeer{ 189 Peer: protocols.NewPeer(p, rw, spec), 190 BzzAddr: handshake.peerAddr, 191 lastActive: time.Now(), 192 LightNode: handshake.LightNode, 193 } 194 195 log.Debug("peer created", "addr", handshake.peerAddr.String()) 196 197 return run(peer) 198 } 199 } 200 201 // performHandshake implements the negotiation of the bzz handshake 202 // shared among swarm subprotocols 203 func (b *Bzz) performHandshake(p *protocols.Peer, handshake *HandshakeMsg) error { 204 ctx, cancel := context.WithTimeout(context.Background(), bzzHandshakeTimeout) 205 defer func() { 206 close(handshake.done) 207 cancel() 208 }() 209 rsh, err := p.Handshake(ctx, handshake, b.checkHandshake) 210 if err != nil { 211 handshake.err = err 212 return err 213 } 214 handshake.peerAddr = rsh.(*HandshakeMsg).Addr 215 handshake.LightNode = rsh.(*HandshakeMsg).LightNode 216 return nil 217 } 218 219 // runBzz is the p2p protocol run function for the bzz base protocol 220 // that negotiates the bzz handshake 221 func (b *Bzz) runBzz(p *p2p.Peer, rw p2p.MsgReadWriter) error { 222 handshake, _ := b.GetOrCreateHandshake(p.ID()) 223 if !<-handshake.init { 224 return fmt.Errorf("%08x: bzz already started on peer %08x", b.localAddr.Over()[:4], p.ID().Bytes()[:4]) 225 } 226 close(handshake.init) 227 defer b.removeHandshake(p.ID()) 228 peer := protocols.NewPeer(p, rw, BzzSpec) 229 err := b.performHandshake(peer, handshake) 230 if err != nil { 231 log.Warn(fmt.Sprintf("%08x: handshake failed with remote peer %08x: %v", b.localAddr.Over()[:4], p.ID().Bytes()[:4], err)) 232 233 return err 234 } 235 // fail if we get another handshake 236 msg, err := rw.ReadMsg() 237 if err != nil { 238 return err 239 } 240 msg.Discard() 241 return errors.New("received multiple handshakes") 242 } 243 244 // BzzPeer is the bzz protocol view of a protocols.Peer (itself an extension of p2p.Peer) 245 // implements the Peer interface and all interfaces Peer implements: Addr, OverlayPeer 246 type BzzPeer struct { 247 *protocols.Peer // represents the connection for online peers 248 *BzzAddr // remote address -> implements Addr interface = protocols.Peer 249 lastActive time.Time // time is updated whenever mutexes are releasing 250 LightNode bool 251 } 252 253 func NewBzzPeer(p *protocols.Peer) *BzzPeer { 254 return &BzzPeer{Peer: p, BzzAddr: NewAddr(p.Node())} 255 } 256 257 // ID returns the peer's underlay node identifier. 258 func (p *BzzPeer) ID() enode.ID { 259 // This is here to resolve a method tie: both protocols.Peer and BzzAddr are embedded 260 // into the struct and provide ID(). The protocols.Peer version is faster, ensure it 261 // gets used. 262 return p.Peer.ID() 263 } 264 265 /* 266 Handshake 267 268 * Version: 8 byte integer version of the protocol 269 * NetworkID: 8 byte integer network identifier 270 * Addr: the address advertised by the node including underlay and overlay connecctions 271 */ 272 type HandshakeMsg struct { 273 Version uint64 274 NetworkID uint64 275 Addr *BzzAddr 276 LightNode bool 277 278 // peerAddr is the address received in the peer handshake 279 peerAddr *BzzAddr 280 281 init chan bool 282 done chan struct{} 283 err error 284 } 285 286 // String pretty prints the handshake 287 func (bh *HandshakeMsg) String() string { 288 return fmt.Sprintf("Handshake: Version: %v, NetworkID: %v, Addr: %v, LightNode: %v, peerAddr: %v", bh.Version, bh.NetworkID, bh.Addr, bh.LightNode, bh.peerAddr) 289 } 290 291 // Perform initiates the handshake and validates the remote handshake message 292 func (b *Bzz) checkHandshake(hs interface{}) error { 293 rhs := hs.(*HandshakeMsg) 294 if rhs.NetworkID != b.NetworkID { 295 return fmt.Errorf("network id mismatch %d (!= %d)", rhs.NetworkID, b.NetworkID) 296 } 297 if rhs.Version != uint64(BzzSpec.Version) { 298 return fmt.Errorf("version mismatch %d (!= %d)", rhs.Version, BzzSpec.Version) 299 } 300 return nil 301 } 302 303 // removeHandshake removes handshake for peer with peerID 304 // from the bzz handshake store 305 func (b *Bzz) removeHandshake(peerID enode.ID) { 306 b.mtx.Lock() 307 defer b.mtx.Unlock() 308 delete(b.handshakes, peerID) 309 } 310 311 // GetHandshake returns the bzz handhake that the remote peer with peerID sent 312 func (b *Bzz) GetOrCreateHandshake(peerID enode.ID) (*HandshakeMsg, bool) { 313 b.mtx.Lock() 314 defer b.mtx.Unlock() 315 handshake, found := b.handshakes[peerID] 316 if !found { 317 handshake = &HandshakeMsg{ 318 Version: uint64(BzzSpec.Version), 319 NetworkID: b.NetworkID, 320 Addr: b.localAddr, 321 LightNode: b.LightNode, 322 init: make(chan bool, 1), 323 done: make(chan struct{}), 324 } 325 // when handhsake is first created for a remote peer 326 // it is initialised with the init 327 handshake.init <- true 328 b.handshakes[peerID] = handshake 329 } 330 331 return handshake, found 332 }