github.com/codingfuture/orig-energi3@v0.8.4/swarm/pss/notify/notify.go (about)

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