github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/swarm/network/messages.go (about)

     1  // This file is part of the go-sberex library. The go-sberex library is 
     2  // free software: you can redistribute it and/or modify it under the terms 
     3  // of the GNU Lesser General Public License as published by the Free 
     4  // Software Foundation, either version 3 of the License, or (at your option)
     5  // any later version.
     6  //
     7  // The go-sberex library is distributed in the hope that it will be useful, 
     8  // but WITHOUT ANY WARRANTY; without even the implied warranty of
     9  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 
    10  // General Public License <http://www.gnu.org/licenses/> for more details.
    11  
    12  package network
    13  
    14  import (
    15  	"fmt"
    16  	"net"
    17  	"time"
    18  
    19  	"github.com/Sberex/go-sberex/contracts/chequebook"
    20  	"github.com/Sberex/go-sberex/p2p/discover"
    21  	"github.com/Sberex/go-sberex/swarm/network/kademlia"
    22  	"github.com/Sberex/go-sberex/swarm/services/swap"
    23  	"github.com/Sberex/go-sberex/swarm/storage"
    24  )
    25  
    26  /*
    27  BZZ protocol Message Types and Message Data Types
    28  */
    29  
    30  // bzz protocol message codes
    31  const (
    32  	statusMsg          = iota // 0x01
    33  	storeRequestMsg           // 0x02
    34  	retrieveRequestMsg        // 0x03
    35  	peersMsg                  // 0x04
    36  	syncRequestMsg            // 0x05
    37  	deliveryRequestMsg        // 0x06
    38  	unsyncedKeysMsg           // 0x07
    39  	paymentMsg                // 0x08
    40  )
    41  
    42  /*
    43   Handshake
    44  
    45  * Version: 8 byte integer version of the protocol
    46  * ID: arbitrary byte sequence client identifier human readable
    47  * Addr: the address advertised by the node, format similar to DEVp2p wire protocol
    48  * Swap: info for the swarm accounting protocol
    49  * NetworkID: 8 byte integer network identifier
    50  * Caps: swarm-specific capabilities, format identical to devp2p
    51  * SyncState: syncronisation state (db iterator key and address space etc) persisted about the peer
    52  
    53  */
    54  type statusMsgData struct {
    55  	Version   uint64
    56  	ID        string
    57  	Addr      *peerAddr
    58  	Swap      *swap.SwapProfile
    59  	NetworkId uint64
    60  }
    61  
    62  func (self *statusMsgData) String() string {
    63  	return fmt.Sprintf("Status: Version: %v, ID: %v, Addr: %v, Swap: %v, NetworkId: %v", self.Version, self.ID, self.Addr, self.Swap, self.NetworkId)
    64  }
    65  
    66  /*
    67   store requests are forwarded to the peers in their kademlia proximity bin
    68   if they are distant
    69   if they are within our storage radius or have any incentive to store it
    70   then attach your nodeID to the metadata
    71   if the storage request is sufficiently close (within our proxLimit, i. e., the
    72   last row of the routing table)
    73  */
    74  type storeRequestMsgData struct {
    75  	Key   storage.Key // hash of datasize | data
    76  	SData []byte      // the actual chunk Data
    77  	// optional
    78  	Id             uint64     // request ID. if delivery, the ID is retrieve request ID
    79  	requestTimeout *time.Time // expiry for forwarding - [not serialised][not currently used]
    80  	storageTimeout *time.Time // expiry of content - [not serialised][not currently used]
    81  	from           *peer      // [not serialised] protocol registers the requester
    82  }
    83  
    84  func (self storeRequestMsgData) String() string {
    85  	var from string
    86  	if self.from == nil {
    87  		from = "self"
    88  	} else {
    89  		from = self.from.Addr().String()
    90  	}
    91  	end := len(self.SData)
    92  	if len(self.SData) > 10 {
    93  		end = 10
    94  	}
    95  	return fmt.Sprintf("from: %v, Key: %v; ID: %v, requestTimeout: %v, storageTimeout: %v, SData %x", from, self.Key, self.Id, self.requestTimeout, self.storageTimeout, self.SData[:end])
    96  }
    97  
    98  /*
    99  Retrieve request
   100  
   101  Timeout in milliseconds. Note that zero timeout retrieval requests do not request forwarding, but prompt for a peers message response. therefore they serve also
   102  as messages to retrieve peers.
   103  
   104  MaxSize specifies the maximum size that the peer will accept. This is useful in
   105  particular if we allow storage and delivery of multichunk payload representing
   106  the entire or partial subtree unfolding from the requested root key.
   107  So when only interested in limited part of a stream (infinite trees) or only
   108  testing chunk availability etc etc, we can indicate it by limiting the size here.
   109  
   110  Request ID can be newly generated or kept from the request originator.
   111  If request ID Is missing or zero, the request is handled as a lookup only
   112  prompting a peers response but not launching a search. Lookup requests are meant
   113  to be used to bootstrap kademlia tables.
   114  
   115  In the special case that the key is the zero value as well, the remote peer's
   116  address is assumed (the message is to be handled as a self lookup request).
   117  The response is a PeersMsg with the peers in the kademlia proximity bin
   118  corresponding to the address.
   119  */
   120  
   121  type retrieveRequestMsgData struct {
   122  	Key      storage.Key // target Key address of chunk to be retrieved
   123  	Id       uint64      // request id, request is a lookup if missing or zero
   124  	MaxSize  uint64      // maximum size of delivery accepted
   125  	MaxPeers uint64      // maximum number of peers returned
   126  	Timeout  uint64      // the longest time we are expecting a response
   127  	timeout  *time.Time  // [not serialied]
   128  	from     *peer       //
   129  }
   130  
   131  func (self *retrieveRequestMsgData) String() string {
   132  	var from string
   133  	if self.from == nil {
   134  		from = "ourselves"
   135  	} else {
   136  		from = self.from.Addr().String()
   137  	}
   138  	var target []byte
   139  	if len(self.Key) > 3 {
   140  		target = self.Key[:4]
   141  	}
   142  	return fmt.Sprintf("from: %v, Key: %x; ID: %v, MaxSize: %v, MaxPeers: %d", from, target, self.Id, self.MaxSize, self.MaxPeers)
   143  }
   144  
   145  // lookups are encoded by missing request ID
   146  func (self *retrieveRequestMsgData) isLookup() bool {
   147  	return self.Id == 0
   148  }
   149  
   150  // sets timeout fields
   151  func (self *retrieveRequestMsgData) setTimeout(t *time.Time) {
   152  	self.timeout = t
   153  	if t != nil {
   154  		self.Timeout = uint64(t.UnixNano())
   155  	} else {
   156  		self.Timeout = 0
   157  	}
   158  }
   159  
   160  func (self *retrieveRequestMsgData) getTimeout() (t *time.Time) {
   161  	if self.Timeout > 0 && self.timeout == nil {
   162  		timeout := time.Unix(int64(self.Timeout), 0)
   163  		t = &timeout
   164  		self.timeout = t
   165  	}
   166  	return
   167  }
   168  
   169  // peerAddr is sent in StatusMsg as part of the handshake
   170  type peerAddr struct {
   171  	IP   net.IP
   172  	Port uint16
   173  	ID   []byte // the 64 byte NodeID (ECDSA Public Key)
   174  	Addr kademlia.Address
   175  }
   176  
   177  // peerAddr pretty prints as enode
   178  func (self *peerAddr) String() string {
   179  	var nodeid discover.NodeID
   180  	copy(nodeid[:], self.ID)
   181  	return discover.NewNode(nodeid, self.IP, 0, self.Port).String()
   182  }
   183  
   184  /*
   185  peers Msg is one response to retrieval; it is always encouraged after a retrieval
   186  request to respond with a list of peers in the same kademlia proximity bin.
   187  The encoding of a peer is identical to that in the devp2p base protocol peers
   188  messages: [IP, Port, NodeID]
   189  note that a node's DPA address is not the NodeID but the hash of the NodeID.
   190  
   191  Timeout serves to indicate whether the responder is forwarding the query within
   192  the timeout or not.
   193  
   194  NodeID serves as the owner of payment contracts and signer of proofs of transfer.
   195  
   196  The Key is the target (if response to a retrieval request) or missing (zero value)
   197  peers address (hash of NodeID) if retrieval request was a self lookup.
   198  
   199  Peers message is requested by retrieval requests with a missing or zero value request ID
   200  */
   201  type peersMsgData struct {
   202  	Peers   []*peerAddr //
   203  	Timeout uint64      //
   204  	timeout *time.Time  // indicate whether responder is expected to deliver content
   205  	Key     storage.Key // present if a response to a retrieval request
   206  	Id      uint64      // present if a response to a retrieval request
   207  	from    *peer
   208  }
   209  
   210  // peers msg pretty printer
   211  func (self *peersMsgData) String() string {
   212  	var from string
   213  	if self.from == nil {
   214  		from = "ourselves"
   215  	} else {
   216  		from = self.from.Addr().String()
   217  	}
   218  	var target []byte
   219  	if len(self.Key) > 3 {
   220  		target = self.Key[:4]
   221  	}
   222  	return fmt.Sprintf("from: %v, Key: %x; ID: %v, Peers: %v", from, target, self.Id, self.Peers)
   223  }
   224  
   225  func (self *peersMsgData) setTimeout(t *time.Time) {
   226  	self.timeout = t
   227  	if t != nil {
   228  		self.Timeout = uint64(t.UnixNano())
   229  	} else {
   230  		self.Timeout = 0
   231  	}
   232  }
   233  
   234  /*
   235  syncRequest
   236  
   237  is sent after the handshake to initiate syncing
   238  the syncState of the remote node is persisted in kaddb and set on the
   239  peer/protocol instance when the node is registered by hive as online{
   240  */
   241  
   242  type syncRequestMsgData struct {
   243  	SyncState *syncState `rlp:"nil"`
   244  }
   245  
   246  func (self *syncRequestMsgData) String() string {
   247  	return fmt.Sprintf("%v", self.SyncState)
   248  }
   249  
   250  /*
   251  deliveryRequest
   252  
   253  is sent once a batch of sync keys is filtered. The ones not found are
   254  sent as a list of syncReuest (hash, priority) in the Deliver field.
   255  When the source receives the sync request it continues to iterate
   256  and fetch at most N items as yet unsynced.
   257  At the same time responds with deliveries of the items.
   258  */
   259  type deliveryRequestMsgData struct {
   260  	Deliver []*syncRequest
   261  }
   262  
   263  func (self *deliveryRequestMsgData) String() string {
   264  	return fmt.Sprintf("sync request for new chunks\ndelivery request for %v chunks", len(self.Deliver))
   265  }
   266  
   267  /*
   268  unsyncedKeys
   269  
   270  is sent first after the handshake if SyncState iterator brings up hundreds, thousands?
   271  and subsequently sent as a response to deliveryRequestMsgData.
   272  
   273  Syncing is the iterative process of exchanging unsyncedKeys and deliveryRequestMsgs
   274  both ways.
   275  
   276  State contains the sync state sent by the source. When the source receives the
   277  sync state it continues to iterate and fetch at most N items as yet unsynced.
   278  At the same time responds with deliveries of the items.
   279  */
   280  type unsyncedKeysMsgData struct {
   281  	Unsynced []*syncRequest
   282  	State    *syncState
   283  }
   284  
   285  func (self *unsyncedKeysMsgData) String() string {
   286  	return fmt.Sprintf("sync: keys of %d new chunks (state %v) => synced: %v", len(self.Unsynced), self.State, self.State.Synced)
   287  }
   288  
   289  /*
   290  payment
   291  
   292  is sent when the swap balance is tilted in favour of the remote peer
   293  and in absolute units exceeds the PayAt parameter in the remote peer's profile
   294  */
   295  
   296  type paymentMsgData struct {
   297  	Units   uint               // units actually paid for (checked against amount by swap)
   298  	Promise *chequebook.Cheque // payment with cheque
   299  }
   300  
   301  func (self *paymentMsgData) String() string {
   302  	return fmt.Sprintf("payment for %d units: %v", self.Units, self.Promise)
   303  }