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