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