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