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] = ¬ifier{ 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 }