github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/swarm/network/protocol.go (about)

     1  // Copyright 2016 The Spectrum Authors
     2  // This file is part of the Spectrum library.
     3  //
     4  // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>.
    16  
    17  package network
    18  
    19  /*
    20  bzz implements the swarm wire protocol [bzz] (sister of eth and shh)
    21  the protocol instance is launched on each peer by the network layer if the
    22  bzz protocol handler is registered on the p2p server.
    23  
    24  The bzz protocol component speaks the bzz protocol
    25  * handle the protocol handshake
    26  * register peers in the KΛÐΞMLIΛ table via the hive logistic manager
    27  * dispatch to hive for handling the DHT logic
    28  * encode and decode requests for storage and retrieval
    29  * handle sync protocol messages via the syncer
    30  * talks the SWAP payment protocol (swap accounting is done within NetStore)
    31  */
    32  
    33  import (
    34  	"errors"
    35  	"fmt"
    36  	"net"
    37  	"strconv"
    38  	"time"
    39  
    40  	"github.com/SmartMeshFoundation/Spectrum/contracts/chequebook"
    41  	"github.com/SmartMeshFoundation/Spectrum/log"
    42  	"github.com/SmartMeshFoundation/Spectrum/p2p"
    43  	bzzswap "github.com/SmartMeshFoundation/Spectrum/swarm/services/swap"
    44  	"github.com/SmartMeshFoundation/Spectrum/swarm/services/swap/swap"
    45  	"github.com/SmartMeshFoundation/Spectrum/swarm/storage"
    46  )
    47  
    48  const (
    49  	Version            = 0
    50  	ProtocolLength     = uint64(8)
    51  	ProtocolMaxMsgSize = 10 * 1024 * 1024
    52  	NetworkId          = 3
    53  )
    54  
    55  // bzz represents the swarm wire protocol
    56  // an instance is running on each peer
    57  type bzz struct {
    58  	storage    StorageHandler       // handler storage/retrieval related requests coming via the bzz wire protocol
    59  	hive       *Hive                // the logistic manager, peerPool, routing service and peer handler
    60  	dbAccess   *DbAccess            // access to db storage counter and iterator for syncing
    61  	requestDb  *storage.LDBDatabase // db to persist backlog of deliveries to aid syncing
    62  	remoteAddr *peerAddr            // remote peers address
    63  	peer       *p2p.Peer            // the p2p peer object
    64  	rw         p2p.MsgReadWriter    // messageReadWriter to send messages to
    65  	backend    chequebook.Backend
    66  	lastActive time.Time
    67  	NetworkId  uint64
    68  
    69  	swap        *swap.Swap          // swap instance for the peer connection
    70  	swapParams  *bzzswap.SwapParams // swap settings both local and remote
    71  	swapEnabled bool                // flag to enable SWAP (will be set via Caps in handshake)
    72  	syncEnabled bool                // flag to enable SYNC (will be set via Caps in handshake)
    73  	syncer      *syncer             // syncer instance for the peer connection
    74  	syncParams  *SyncParams         // syncer params
    75  	syncState   *syncState          // outgoing syncronisation state (contains reference to remote peers db counter)
    76  }
    77  
    78  // interface type for handler of storage/retrieval related requests coming
    79  // via the bzz wire protocol
    80  // messages: UnsyncedKeys, DeliveryRequest, StoreRequest, RetrieveRequest
    81  type StorageHandler interface {
    82  	HandleUnsyncedKeysMsg(req *unsyncedKeysMsgData, p *peer) error
    83  	HandleDeliveryRequestMsg(req *deliveryRequestMsgData, p *peer) error
    84  	HandleStoreRequestMsg(req *storeRequestMsgData, p *peer)
    85  	HandleRetrieveRequestMsg(req *retrieveRequestMsgData, p *peer)
    86  }
    87  
    88  /*
    89  main entrypoint, wrappers starting a server that will run the bzz protocol
    90  use this constructor to attach the protocol ("class") to server caps
    91  This is done by node.Node#Register(func(node.ServiceContext) (Service, error))
    92  Service implements Protocols() which is an array of protocol constructors
    93  at node startup the protocols are initialised
    94  the Dev p2p layer then calls Run(p *p2p.Peer, rw p2p.MsgReadWriter) error
    95  on each peer connection
    96  The Run function of the Bzz protocol class creates a bzz instance
    97  which will represent the peer for the swarm hive and all peer-aware components
    98  */
    99  func Bzz(cloud StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64) (p2p.Protocol, error) {
   100  
   101  	// a single global request db is created for all peer connections
   102  	// this is to persist delivery backlog and aid syncronisation
   103  	requestDb, err := storage.NewLDBDatabase(sy.RequestDbPath)
   104  	if err != nil {
   105  		return p2p.Protocol{}, fmt.Errorf("error setting up request db: %v", err)
   106  	}
   107  	if networkId == 0 {
   108  		networkId = NetworkId
   109  	}
   110  	return p2p.Protocol{
   111  		Name:    "bzz",
   112  		Version: Version,
   113  		Length:  ProtocolLength,
   114  		Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
   115  			return run(requestDb, cloud, backend, hive, dbaccess, sp, sy, networkId, p, rw)
   116  		},
   117  	}, nil
   118  }
   119  
   120  /*
   121  the main protocol loop that
   122   * does the handshake by exchanging statusMsg
   123   * if peer is valid and accepted, registers with the hive
   124   * then enters into a forever loop handling incoming messages
   125   * storage and retrieval related queries coming via bzz are dispatched to StorageHandler
   126   * peer-related messages are dispatched to the hive
   127   * payment related messages are relayed to SWAP service
   128   * on disconnect, unregister the peer in the hive (note RemovePeer in the post-disconnect hook)
   129   * whenever the loop terminates, the peer will disconnect with Subprotocol error
   130   * whenever handlers return an error the loop terminates
   131  */
   132  func run(requestDb *storage.LDBDatabase, depo StorageHandler, backend chequebook.Backend, hive *Hive, dbaccess *DbAccess, sp *bzzswap.SwapParams, sy *SyncParams, networkId uint64, p *p2p.Peer, rw p2p.MsgReadWriter) (err error) {
   133  
   134  	self := &bzz{
   135  		storage:     depo,
   136  		backend:     backend,
   137  		hive:        hive,
   138  		dbAccess:    dbaccess,
   139  		requestDb:   requestDb,
   140  		peer:        p,
   141  		rw:          rw,
   142  		swapParams:  sp,
   143  		syncParams:  sy,
   144  		swapEnabled: hive.swapEnabled,
   145  		syncEnabled: true,
   146  		NetworkId:   networkId,
   147  	}
   148  
   149  	// handle handshake
   150  	err = self.handleStatus()
   151  	if err != nil {
   152  		return err
   153  	}
   154  	defer func() {
   155  		// if the handler loop exits, the peer is disconnecting
   156  		// deregister the peer in the hive
   157  		self.hive.removePeer(&peer{bzz: self})
   158  		if self.syncer != nil {
   159  			self.syncer.stop() // quits request db and delivery loops, save requests
   160  		}
   161  		if self.swap != nil {
   162  			self.swap.Stop() // quits chequebox autocash etc
   163  		}
   164  	}()
   165  
   166  	// the main forever loop that handles incoming requests
   167  	for {
   168  		if self.hive.blockRead {
   169  			log.Warn(fmt.Sprintf("Cannot read network"))
   170  			time.Sleep(100 * time.Millisecond)
   171  			continue
   172  		}
   173  		err = self.handle()
   174  		if err != nil {
   175  			return
   176  		}
   177  	}
   178  }
   179  
   180  // TODO: may need to implement protocol drop only? don't want to kick off the peer
   181  // if they are useful for other protocols
   182  func (self *bzz) Drop() {
   183  	self.peer.Disconnect(p2p.DiscSubprotocolError)
   184  }
   185  
   186  // one cycle of the main forever loop that handles and dispatches incoming messages
   187  func (self *bzz) handle() error {
   188  	msg, err := self.rw.ReadMsg()
   189  	log.Debug(fmt.Sprintf("<- %v", msg))
   190  	if err != nil {
   191  		return err
   192  	}
   193  	if msg.Size > ProtocolMaxMsgSize {
   194  		return fmt.Errorf("message too long: %v > %v", msg.Size, ProtocolMaxMsgSize)
   195  	}
   196  	// make sure that the payload has been fully consumed
   197  	defer msg.Discard()
   198  
   199  	switch msg.Code {
   200  
   201  	case statusMsg:
   202  		// no extra status message allowed. The one needed already handled by
   203  		// handleStatus
   204  		log.Debug(fmt.Sprintf("Status message: %v", msg))
   205  		return errors.New("extra status message")
   206  
   207  	case storeRequestMsg:
   208  		// store requests are dispatched to netStore
   209  		var req storeRequestMsgData
   210  		if err := msg.Decode(&req); err != nil {
   211  			return fmt.Errorf("<- %v: %v", msg, err)
   212  		}
   213  		if n := len(req.SData); n < 9 {
   214  			return fmt.Errorf("<- %v: Data too short (%v)", msg, n)
   215  		}
   216  		// last Active time is set only when receiving chunks
   217  		self.lastActive = time.Now()
   218  		log.Trace(fmt.Sprintf("incoming store request: %s", req.String()))
   219  		// swap accounting is done within forwarding
   220  		self.storage.HandleStoreRequestMsg(&req, &peer{bzz: self})
   221  
   222  	case retrieveRequestMsg:
   223  		// retrieve Requests are dispatched to netStore
   224  		var req retrieveRequestMsgData
   225  		if err := msg.Decode(&req); err != nil {
   226  			return fmt.Errorf("<- %v: %v", msg, err)
   227  		}
   228  		req.from = &peer{bzz: self}
   229  		// if request is lookup and not to be delivered
   230  		if req.isLookup() {
   231  			log.Trace(fmt.Sprintf("self lookup for %v: responding with peers only...", req.from))
   232  		} else if req.Key == nil {
   233  			return fmt.Errorf("protocol handler: req.Key == nil || req.Timeout == nil")
   234  		} else {
   235  			// swap accounting is done within netStore
   236  			self.storage.HandleRetrieveRequestMsg(&req, &peer{bzz: self})
   237  		}
   238  		// direct response with peers, TODO: sort this out
   239  		self.hive.peers(&req)
   240  
   241  	case peersMsg:
   242  		// response to lookups and immediate response to retrieve requests
   243  		// dispatches new peer data to the hive that adds them to KADDB
   244  		var req peersMsgData
   245  		if err := msg.Decode(&req); err != nil {
   246  			return fmt.Errorf("<- %v: %v", msg, err)
   247  		}
   248  		req.from = &peer{bzz: self}
   249  		log.Trace(fmt.Sprintf("<- peer addresses: %v", req))
   250  		self.hive.HandlePeersMsg(&req, &peer{bzz: self})
   251  
   252  	case syncRequestMsg:
   253  		var req syncRequestMsgData
   254  		if err := msg.Decode(&req); err != nil {
   255  			return fmt.Errorf("<- %v: %v", msg, err)
   256  		}
   257  		log.Debug(fmt.Sprintf("<- sync request: %v", req))
   258  		self.lastActive = time.Now()
   259  		self.sync(req.SyncState)
   260  
   261  	case unsyncedKeysMsg:
   262  		// coming from parent node offering
   263  		var req unsyncedKeysMsgData
   264  		if err := msg.Decode(&req); err != nil {
   265  			return fmt.Errorf("<- %v: %v", msg, err)
   266  		}
   267  		log.Debug(fmt.Sprintf("<- unsynced keys : %s", req.String()))
   268  		err := self.storage.HandleUnsyncedKeysMsg(&req, &peer{bzz: self})
   269  		self.lastActive = time.Now()
   270  		if err != nil {
   271  			return fmt.Errorf("<- %v: %v", msg, err)
   272  		}
   273  
   274  	case deliveryRequestMsg:
   275  		// response to syncKeysMsg hashes filtered not existing in db
   276  		// also relays the last synced state to the source
   277  		var req deliveryRequestMsgData
   278  		if err := msg.Decode(&req); err != nil {
   279  			return fmt.Errorf("<-msg %v: %v", msg, err)
   280  		}
   281  		log.Debug(fmt.Sprintf("<- delivery request: %s", req.String()))
   282  		err := self.storage.HandleDeliveryRequestMsg(&req, &peer{bzz: self})
   283  		self.lastActive = time.Now()
   284  		if err != nil {
   285  			return fmt.Errorf("<- %v: %v", msg, err)
   286  		}
   287  
   288  	case paymentMsg:
   289  		// swap protocol message for payment, Units paid for, Cheque paid with
   290  		if self.swapEnabled {
   291  			var req paymentMsgData
   292  			if err := msg.Decode(&req); err != nil {
   293  				return fmt.Errorf("<- %v: %v", msg, err)
   294  			}
   295  			log.Debug(fmt.Sprintf("<- payment: %s", req.String()))
   296  			self.swap.Receive(int(req.Units), req.Promise)
   297  		}
   298  
   299  	default:
   300  		// no other message is allowed
   301  		return fmt.Errorf("invalid message code: %v", msg.Code)
   302  	}
   303  	return nil
   304  }
   305  
   306  func (self *bzz) handleStatus() (err error) {
   307  
   308  	handshake := &statusMsgData{
   309  		Version:   uint64(Version),
   310  		ID:        "honey",
   311  		Addr:      self.selfAddr(),
   312  		NetworkId: self.NetworkId,
   313  		Swap: &bzzswap.SwapProfile{
   314  			Profile:    self.swapParams.Profile,
   315  			PayProfile: self.swapParams.PayProfile,
   316  		},
   317  	}
   318  
   319  	err = p2p.Send(self.rw, statusMsg, handshake)
   320  	if err != nil {
   321  		return err
   322  	}
   323  
   324  	// read and handle remote status
   325  	var msg p2p.Msg
   326  	msg, err = self.rw.ReadMsg()
   327  	if err != nil {
   328  		return err
   329  	}
   330  
   331  	if msg.Code != statusMsg {
   332  		return fmt.Errorf("first msg has code %x (!= %x)", msg.Code, statusMsg)
   333  	}
   334  
   335  	if msg.Size > ProtocolMaxMsgSize {
   336  		return fmt.Errorf("message too long: %v > %v", msg.Size, ProtocolMaxMsgSize)
   337  	}
   338  
   339  	var status statusMsgData
   340  	if err := msg.Decode(&status); err != nil {
   341  		return fmt.Errorf("<- %v: %v", msg, err)
   342  	}
   343  
   344  	if status.NetworkId != self.NetworkId {
   345  		return fmt.Errorf("network id mismatch: %d (!= %d)", status.NetworkId, self.NetworkId)
   346  	}
   347  
   348  	if Version != status.Version {
   349  		return fmt.Errorf("protocol version mismatch: %d (!= %d)", status.Version, Version)
   350  	}
   351  
   352  	self.remoteAddr = self.peerAddr(status.Addr)
   353  	log.Trace(fmt.Sprintf("self: advertised IP: %v, peer advertised: %v, local address: %v\npeer: advertised IP: %v, remote address: %v\n", self.selfAddr(), self.remoteAddr, self.peer.LocalAddr(), status.Addr.IP, self.peer.RemoteAddr()))
   354  
   355  	if self.swapEnabled {
   356  		// set remote profile for accounting
   357  		self.swap, err = bzzswap.NewSwap(self.swapParams, status.Swap, self.backend, self)
   358  		if err != nil {
   359  			return err
   360  		}
   361  	}
   362  
   363  	log.Info(fmt.Sprintf("Peer %08x is capable (%d/%d)", self.remoteAddr.Addr[:4], status.Version, status.NetworkId))
   364  	err = self.hive.addPeer(&peer{bzz: self})
   365  	if err != nil {
   366  		return err
   367  	}
   368  
   369  	// hive sets syncstate so sync should start after node added
   370  	log.Info(fmt.Sprintf("syncronisation request sent with %v", self.syncState))
   371  	self.syncRequest()
   372  
   373  	return nil
   374  }
   375  
   376  func (self *bzz) sync(state *syncState) error {
   377  	// syncer setup
   378  	if self.syncer != nil {
   379  		return errors.New("sync request can only be sent once")
   380  	}
   381  
   382  	cnt := self.dbAccess.counter()
   383  	remoteaddr := self.remoteAddr.Addr
   384  	start, stop := self.hive.kad.KeyRange(remoteaddr)
   385  
   386  	// an explicitly received nil syncstate disables syncronisation
   387  	if state == nil {
   388  		self.syncEnabled = false
   389  		log.Warn(fmt.Sprintf("syncronisation disabled for peer %v", self))
   390  		state = &syncState{DbSyncState: &storage.DbSyncState{}, Synced: true}
   391  	} else {
   392  		state.synced = make(chan bool)
   393  		state.SessionAt = cnt
   394  		if storage.IsZeroKey(state.Stop) && state.Synced {
   395  			state.Start = storage.Key(start[:])
   396  			state.Stop = storage.Key(stop[:])
   397  		}
   398  		log.Debug(fmt.Sprintf("syncronisation requested by peer %v at state %v", self, state))
   399  	}
   400  	var err error
   401  	self.syncer, err = newSyncer(
   402  		self.requestDb,
   403  		storage.Key(remoteaddr[:]),
   404  		self.dbAccess,
   405  		self.unsyncedKeys, self.store,
   406  		self.syncParams, state, func() bool { return self.syncEnabled },
   407  	)
   408  	if err != nil {
   409  		return nil
   410  	}
   411  	log.Trace(fmt.Sprintf("syncer set for peer %v", self))
   412  	return nil
   413  }
   414  
   415  func (self *bzz) String() string {
   416  	return self.remoteAddr.String()
   417  }
   418  
   419  // repair reported address if IP missing
   420  func (self *bzz) peerAddr(base *peerAddr) *peerAddr {
   421  	if base.IP.IsUnspecified() {
   422  		host, _, _ := net.SplitHostPort(self.peer.RemoteAddr().String())
   423  		base.IP = net.ParseIP(host)
   424  	}
   425  	return base
   426  }
   427  
   428  // returns self advertised node connection info (listening address w enodes)
   429  // IP will get repaired on the other end if missing
   430  // or resolved via ID by discovery at dialout
   431  func (self *bzz) selfAddr() *peerAddr {
   432  	id := self.hive.id
   433  	host, port, _ := net.SplitHostPort(self.hive.listenAddr())
   434  	intport, _ := strconv.Atoi(port)
   435  	addr := &peerAddr{
   436  		Addr: self.hive.addr,
   437  		ID:   id[:],
   438  		IP:   net.ParseIP(host),
   439  		Port: uint16(intport),
   440  	}
   441  	return addr
   442  }
   443  
   444  // outgoing messages
   445  // send retrieveRequestMsg
   446  func (self *bzz) retrieve(req *retrieveRequestMsgData) error {
   447  	return self.send(retrieveRequestMsg, req)
   448  }
   449  
   450  // send storeRequestMsg
   451  func (self *bzz) store(req *storeRequestMsgData) error {
   452  	return self.send(storeRequestMsg, req)
   453  }
   454  
   455  func (self *bzz) syncRequest() error {
   456  	req := &syncRequestMsgData{}
   457  	if self.hive.syncEnabled {
   458  		log.Debug(fmt.Sprintf("syncronisation request to peer %v at state %v", self, self.syncState))
   459  		req.SyncState = self.syncState
   460  	}
   461  	if self.syncState == nil {
   462  		log.Warn(fmt.Sprintf("syncronisation disabled for peer %v at state %v", self, self.syncState))
   463  	}
   464  	return self.send(syncRequestMsg, req)
   465  }
   466  
   467  // queue storeRequestMsg in request db
   468  func (self *bzz) deliveryRequest(reqs []*syncRequest) error {
   469  	req := &deliveryRequestMsgData{
   470  		Deliver: reqs,
   471  	}
   472  	return self.send(deliveryRequestMsg, req)
   473  }
   474  
   475  // batch of syncRequests to send off
   476  func (self *bzz) unsyncedKeys(reqs []*syncRequest, state *syncState) error {
   477  	req := &unsyncedKeysMsgData{
   478  		Unsynced: reqs,
   479  		State:    state,
   480  	}
   481  	return self.send(unsyncedKeysMsg, req)
   482  }
   483  
   484  // send paymentMsg
   485  func (self *bzz) Pay(units int, promise swap.Promise) {
   486  	req := &paymentMsgData{uint(units), promise.(*chequebook.Cheque)}
   487  	self.payment(req)
   488  }
   489  
   490  // send paymentMsg
   491  func (self *bzz) payment(req *paymentMsgData) error {
   492  	return self.send(paymentMsg, req)
   493  }
   494  
   495  // sends peersMsg
   496  func (self *bzz) peers(req *peersMsgData) error {
   497  	return self.send(peersMsg, req)
   498  }
   499  
   500  func (self *bzz) send(msg uint64, data interface{}) error {
   501  	if self.hive.blockWrite {
   502  		return fmt.Errorf("network write blocked")
   503  	}
   504  	log.Trace(fmt.Sprintf("-> %v: %v (%T) to %v", msg, data, data, self))
   505  	err := p2p.Send(self.rw, msg, data)
   506  	if err != nil {
   507  		self.Drop()
   508  	}
   509  	return err
   510  }