github.com/FUSIONFoundation/efsn@v3.6.2-0.20200916075423-dbb5dd5d2cc7+incompatible/swarm/pss/notify/notify.go (about)

     1  package notify
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"fmt"
     6  	"sync"
     7  
     8  	"github.com/FusionFoundation/efsn/common/hexutil"
     9  	"github.com/FusionFoundation/efsn/crypto"
    10  	"github.com/FusionFoundation/efsn/p2p"
    11  	"github.com/FusionFoundation/efsn/rlp"
    12  	"github.com/FusionFoundation/efsn/swarm/log"
    13  	"github.com/FusionFoundation/efsn/swarm/pss"
    14  )
    15  
    16  const (
    17  	// sent from requester to updater to request start of notifications
    18  	MsgCodeStart = iota
    19  
    20  	// sent from updater to requester, contains a notification plus a new symkey to replace the old
    21  	MsgCodeNotifyWithKey
    22  
    23  	// sent from updater to requester, contains a notification
    24  	MsgCodeNotify
    25  
    26  	// sent from requester to updater to request stop of notifications (currently unused)
    27  	MsgCodeStop
    28  	MsgCodeMax
    29  )
    30  
    31  const (
    32  	DefaultAddressLength = 1
    33  	symKeyLength         = 32 // this should be gotten from source
    34  )
    35  
    36  var (
    37  	// control topic is used before symmetric key issuance completes
    38  	controlTopic = pss.Topic{0x00, 0x00, 0x00, 0x01}
    39  )
    40  
    41  // when code is MsgCodeStart, Payload is address
    42  // when code is MsgCodeNotifyWithKey, Payload is notification | symkey
    43  // when code is MsgCodeNotify, Payload is notification
    44  // when code is MsgCodeStop, Payload is address
    45  type Msg struct {
    46  	Code       byte
    47  	Name       []byte
    48  	Payload    []byte
    49  	namestring string
    50  }
    51  
    52  // NewMsg creates a new notification message object
    53  func NewMsg(code byte, name string, payload []byte) *Msg {
    54  	return &Msg{
    55  		Code:       code,
    56  		Name:       []byte(name),
    57  		Payload:    payload,
    58  		namestring: name,
    59  	}
    60  }
    61  
    62  // NewMsgFromPayload decodes a serialized message payload into a new notification message object
    63  func NewMsgFromPayload(payload []byte) (*Msg, error) {
    64  	msg := &Msg{}
    65  	err := rlp.DecodeBytes(payload, msg)
    66  	if err != nil {
    67  		return nil, err
    68  	}
    69  	msg.namestring = string(msg.Name)
    70  	return msg, nil
    71  }
    72  
    73  // a notifier has one sendBin entry for each address space it sends messages to
    74  type sendBin struct {
    75  	address  pss.PssAddress
    76  	symKeyId string
    77  	count    int
    78  }
    79  
    80  // represents a single notification service
    81  // only subscription address bins that match the address of a notification client have entries.
    82  type notifier struct {
    83  	bins      map[string]*sendBin
    84  	topic     pss.Topic // identifies the resource for pss receiver
    85  	threshold int       // amount of address bytes used in bins
    86  	updateC   <-chan []byte
    87  	quitC     chan struct{}
    88  }
    89  
    90  func (n *notifier) removeSubscription() {
    91  	n.quitC <- struct{}{}
    92  }
    93  
    94  // represents an individual subscription made by a public key at a specific address/neighborhood
    95  type subscription struct {
    96  	pubkeyId string
    97  	address  pss.PssAddress
    98  	handler  func(string, []byte) error
    99  }
   100  
   101  // Controller is the interface to control, add and remove notification services and subscriptions
   102  type Controller struct {
   103  	pss           *pss.Pss
   104  	notifiers     map[string]*notifier
   105  	subscriptions map[string]*subscription
   106  	mu            sync.Mutex
   107  }
   108  
   109  // NewController creates a new Controller object
   110  func NewController(ps *pss.Pss) *Controller {
   111  	ctrl := &Controller{
   112  		pss:           ps,
   113  		notifiers:     make(map[string]*notifier),
   114  		subscriptions: make(map[string]*subscription),
   115  	}
   116  	ctrl.pss.Register(&controlTopic, ctrl.Handler)
   117  	return ctrl
   118  }
   119  
   120  // IsActive is used to check if a notification service exists for a specified id string
   121  // Returns true if exists, false if not
   122  func (c *Controller) IsActive(name string) bool {
   123  	c.mu.Lock()
   124  	defer c.mu.Unlock()
   125  	return c.isActive(name)
   126  }
   127  
   128  func (c *Controller) isActive(name string) bool {
   129  	_, ok := c.notifiers[name]
   130  	return ok
   131  }
   132  
   133  // Subscribe is used by a client to request notifications from a notification service provider
   134  // It will create a MsgCodeStart message and send asymmetrically to the provider using its public key and routing address
   135  // The handler function is a callback that will be called when notifications are received
   136  // Fails if the request pss cannot be sent or if the update message could not be serialized
   137  func (c *Controller) Subscribe(name string, pubkey *ecdsa.PublicKey, address pss.PssAddress, handler func(string, []byte) error) error {
   138  	c.mu.Lock()
   139  	defer c.mu.Unlock()
   140  	msg := NewMsg(MsgCodeStart, name, c.pss.BaseAddr())
   141  	c.pss.SetPeerPublicKey(pubkey, controlTopic, &address)
   142  	pubkeyId := hexutil.Encode(crypto.FromECDSAPub(pubkey))
   143  	smsg, err := rlp.EncodeToBytes(msg)
   144  	if err != nil {
   145  		return err
   146  	}
   147  	err = c.pss.SendAsym(pubkeyId, controlTopic, smsg)
   148  	if err != nil {
   149  		return err
   150  	}
   151  	c.subscriptions[name] = &subscription{
   152  		pubkeyId: pubkeyId,
   153  		address:  address,
   154  		handler:  handler,
   155  	}
   156  	return nil
   157  }
   158  
   159  // Unsubscribe, perhaps unsurprisingly, undoes the effects of Subscribe
   160  // Fails if the subscription does not exist, if the request pss cannot be sent or if the update message could not be serialized
   161  func (c *Controller) Unsubscribe(name string) error {
   162  	c.mu.Lock()
   163  	defer c.mu.Unlock()
   164  	sub, ok := c.subscriptions[name]
   165  	if !ok {
   166  		return fmt.Errorf("Unknown subscription '%s'", name)
   167  	}
   168  	msg := NewMsg(MsgCodeStop, name, sub.address)
   169  	smsg, err := rlp.EncodeToBytes(msg)
   170  	if err != nil {
   171  		return err
   172  	}
   173  	err = c.pss.SendAsym(sub.pubkeyId, controlTopic, smsg)
   174  	if err != nil {
   175  		return err
   176  	}
   177  	delete(c.subscriptions, name)
   178  	return nil
   179  }
   180  
   181  // NewNotifier is used by a notification service provider to create a new notification service
   182  // It takes a name as identifier for the resource, a threshold indicating the granularity of the subscription address bin
   183  // It then starts an event loop which listens to the supplied update channel and executes notifications on channel receives
   184  // Fails if a notifier already is registered on the name
   185  //func (c *Controller) NewNotifier(name string, threshold int, contentFunc func(string) ([]byte, error)) error {
   186  func (c *Controller) NewNotifier(name string, threshold int, updateC <-chan []byte) (func(), error) {
   187  	c.mu.Lock()
   188  	if c.isActive(name) {
   189  		c.mu.Unlock()
   190  		return nil, fmt.Errorf("Notification service %s already exists in controller", name)
   191  	}
   192  	quitC := make(chan struct{})
   193  	c.notifiers[name] = &notifier{
   194  		bins:      make(map[string]*sendBin),
   195  		topic:     pss.BytesToTopic([]byte(name)),
   196  		threshold: threshold,
   197  		updateC:   updateC,
   198  		quitC:     quitC,
   199  		//contentFunc: contentFunc,
   200  	}
   201  	c.mu.Unlock()
   202  	go func() {
   203  		for {
   204  			select {
   205  			case <-quitC:
   206  				return
   207  			case data := <-updateC:
   208  				c.notify(name, data)
   209  			}
   210  		}
   211  	}()
   212  
   213  	return c.notifiers[name].removeSubscription, nil
   214  }
   215  
   216  // RemoveNotifier is used to stop a notification service.
   217  // It cancels the event loop listening to the notification provider's update channel
   218  func (c *Controller) RemoveNotifier(name string) error {
   219  	c.mu.Lock()
   220  	defer c.mu.Unlock()
   221  	currentNotifier, ok := c.notifiers[name]
   222  	if !ok {
   223  		return fmt.Errorf("Unknown notification service %s", name)
   224  	}
   225  	currentNotifier.removeSubscription()
   226  	delete(c.notifiers, name)
   227  	return nil
   228  }
   229  
   230  // Notify is called by a notification service provider to issue a new notification
   231  // It takes the name of the notification service and the data to be sent.
   232  // It fails if a notifier with this name does not exist or if data could not be serialized
   233  // Note that it does NOT fail on failure to send a message
   234  func (c *Controller) notify(name string, data []byte) error {
   235  	c.mu.Lock()
   236  	defer c.mu.Unlock()
   237  	if !c.isActive(name) {
   238  		return fmt.Errorf("Notification service %s doesn't exist", name)
   239  	}
   240  	msg := NewMsg(MsgCodeNotify, name, data)
   241  	smsg, err := rlp.EncodeToBytes(msg)
   242  	if err != nil {
   243  		return err
   244  	}
   245  	for _, m := range c.notifiers[name].bins {
   246  		log.Debug("sending pss notify", "name", name, "addr", fmt.Sprintf("%x", m.address), "topic", fmt.Sprintf("%x", c.notifiers[name].topic), "data", data)
   247  		go func(m *sendBin) {
   248  			err = c.pss.SendSym(m.symKeyId, c.notifiers[name].topic, smsg)
   249  			if err != nil {
   250  				log.Warn("Failed to send notify to addr %x: %v", m.address, err)
   251  			}
   252  		}(m)
   253  	}
   254  	return nil
   255  }
   256  
   257  // check if we already have the bin
   258  // if we do, retrieve the symkey from it and increment the count
   259  // if we dont make a new symkey and a new bin entry
   260  func (c *Controller) addToBin(ntfr *notifier, address []byte) (symKeyId string, pssAddress pss.PssAddress, err error) {
   261  
   262  	// parse the address from the message and truncate if longer than our bins threshold
   263  	if len(address) > ntfr.threshold {
   264  		address = address[:ntfr.threshold]
   265  	}
   266  
   267  	pssAddress = pss.PssAddress(address)
   268  	hexAddress := fmt.Sprintf("%x", address)
   269  	currentBin, ok := ntfr.bins[hexAddress]
   270  	if ok {
   271  		currentBin.count++
   272  		symKeyId = currentBin.symKeyId
   273  	} else {
   274  		symKeyId, err = c.pss.GenerateSymmetricKey(ntfr.topic, &pssAddress, false)
   275  		if err != nil {
   276  			return "", nil, err
   277  		}
   278  		ntfr.bins[hexAddress] = &sendBin{
   279  			address:  address,
   280  			symKeyId: symKeyId,
   281  			count:    1,
   282  		}
   283  	}
   284  	return symKeyId, pssAddress, nil
   285  }
   286  
   287  func (c *Controller) handleStartMsg(msg *Msg, keyid string) (err error) {
   288  
   289  	keyidbytes, err := hexutil.Decode(keyid)
   290  	if err != nil {
   291  		return err
   292  	}
   293  	pubkey, err := crypto.UnmarshalPubkey(keyidbytes)
   294  	if err != nil {
   295  		return err
   296  	}
   297  
   298  	// if name is not registered for notifications we will not react
   299  	currentNotifier, ok := c.notifiers[msg.namestring]
   300  	if !ok {
   301  		return fmt.Errorf("Subscribe attempted on unknown resource '%s'", msg.namestring)
   302  	}
   303  
   304  	// add to or open new bin
   305  	symKeyId, pssAddress, err := c.addToBin(currentNotifier, msg.Payload)
   306  	if err != nil {
   307  		return err
   308  	}
   309  
   310  	// add to address book for send initial notify
   311  	symkey, err := c.pss.GetSymmetricKey(symKeyId)
   312  	if err != nil {
   313  		return err
   314  	}
   315  	err = c.pss.SetPeerPublicKey(pubkey, controlTopic, &pssAddress)
   316  	if err != nil {
   317  		return err
   318  	}
   319  
   320  	// TODO this is set to zero-length byte pending decision on protocol for initial message, whether it should include message or not, and how to trigger the initial message so that current state of MRU is sent upon subscription
   321  	notify := []byte{}
   322  	replyMsg := NewMsg(MsgCodeNotifyWithKey, msg.namestring, make([]byte, len(notify)+symKeyLength))
   323  	copy(replyMsg.Payload, notify)
   324  	copy(replyMsg.Payload[len(notify):], symkey)
   325  	sReplyMsg, err := rlp.EncodeToBytes(replyMsg)
   326  	if err != nil {
   327  		return err
   328  	}
   329  	return c.pss.SendAsym(keyid, controlTopic, sReplyMsg)
   330  }
   331  
   332  func (c *Controller) handleNotifyWithKeyMsg(msg *Msg) error {
   333  	symkey := msg.Payload[len(msg.Payload)-symKeyLength:]
   334  	topic := pss.BytesToTopic(msg.Name)
   335  
   336  	// \TODO keep track of and add actual address
   337  	updaterAddr := pss.PssAddress([]byte{})
   338  	c.pss.SetSymmetricKey(symkey, topic, &updaterAddr, true)
   339  	c.pss.Register(&topic, c.Handler)
   340  	return c.subscriptions[msg.namestring].handler(msg.namestring, msg.Payload[:len(msg.Payload)-symKeyLength])
   341  }
   342  
   343  func (c *Controller) handleStopMsg(msg *Msg) error {
   344  	// if name is not registered for notifications we will not react
   345  	currentNotifier, ok := c.notifiers[msg.namestring]
   346  	if !ok {
   347  		return fmt.Errorf("Unsubscribe attempted on unknown resource '%s'", msg.namestring)
   348  	}
   349  
   350  	// parse the address from the message and truncate if longer than our bins' address length threshold
   351  	address := msg.Payload
   352  	if len(msg.Payload) > currentNotifier.threshold {
   353  		address = address[:currentNotifier.threshold]
   354  	}
   355  
   356  	// remove the entry from the bin if it exists, and remove the bin if it's the last remaining one
   357  	hexAddress := fmt.Sprintf("%x", address)
   358  	currentBin, ok := currentNotifier.bins[hexAddress]
   359  	if !ok {
   360  		return fmt.Errorf("found no active bin for address %s", hexAddress)
   361  	}
   362  	currentBin.count--
   363  	if currentBin.count == 0 { // if no more clients in this bin, remove it
   364  		delete(currentNotifier.bins, hexAddress)
   365  	}
   366  	return nil
   367  }
   368  
   369  // Handler is the pss topic handler to be used to process notification service messages
   370  // It should be registered in the pss of both to any notification service provides and clients using the service
   371  func (c *Controller) Handler(smsg []byte, p *p2p.Peer, asymmetric bool, keyid string) error {
   372  	c.mu.Lock()
   373  	defer c.mu.Unlock()
   374  	log.Debug("notify controller handler", "keyid", keyid)
   375  
   376  	// see if the message is valid
   377  	msg, err := NewMsgFromPayload(smsg)
   378  	if err != nil {
   379  		return err
   380  	}
   381  
   382  	switch msg.Code {
   383  	case MsgCodeStart:
   384  		return c.handleStartMsg(msg, keyid)
   385  	case MsgCodeNotifyWithKey:
   386  		return c.handleNotifyWithKeyMsg(msg)
   387  	case MsgCodeNotify:
   388  		return c.subscriptions[msg.namestring].handler(msg.namestring, msg.Payload)
   389  	case MsgCodeStop:
   390  		return c.handleStopMsg(msg)
   391  	}
   392  
   393  	return fmt.Errorf("Invalid message code: %d", msg.Code)
   394  }