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