github.com/status-im/status-go@v1.1.0/protocol/encryption/publisher/publisher.go (about) 1 package publisher 2 3 import ( 4 "crypto/ecdsa" 5 "errors" 6 "time" 7 8 "go.uber.org/zap" 9 10 "github.com/status-im/status-go/eth-node/crypto" 11 "github.com/status-im/status-go/logutils" 12 ) 13 14 const ( 15 // How often a ticker fires in seconds. 16 tickerInterval = 120 17 // How often we should publish a contact code in seconds. 18 publishInterval = 21600 19 // Cooldown period on acking messages when not targeting our device. 20 deviceNotFoundAckInterval = 7200 21 ) 22 23 var ( 24 errNotEnoughTimePassed = errors.New("not enough time passed") 25 ) 26 27 type Publisher struct { 28 persistence *persistence 29 logger *zap.Logger 30 notifyCh chan struct{} 31 quit chan struct{} 32 } 33 34 func New(logger *zap.Logger) *Publisher { 35 if logger == nil { 36 logger = logutils.ZapLogger() 37 } 38 39 return &Publisher{ 40 persistence: newPersistence(), 41 logger: logger.With(zap.Namespace("Publisher")), 42 } 43 } 44 45 func (p *Publisher) Start() <-chan struct{} { 46 logger := p.logger.With(zap.String("site", "Start")) 47 48 logger.Info("starting publisher") 49 50 p.notifyCh = make(chan struct{}, 100) 51 p.quit = make(chan struct{}) 52 53 go p.tickerLoop() 54 55 return p.notifyCh 56 } 57 58 func (p *Publisher) Stop() { 59 // If hasn't started, ignore 60 if p.quit == nil { 61 return 62 } 63 select { 64 case _, ok := <-p.quit: 65 if !ok { 66 // channel already closed 67 return 68 } 69 default: 70 close(p.quit) 71 } 72 } 73 74 func (p *Publisher) tickerLoop() { 75 ticker := time.NewTicker(tickerInterval * time.Second) 76 77 go func() { 78 logger := p.logger.With(zap.String("site", "tickerLoop")) 79 80 for { 81 select { 82 case <-ticker.C: 83 err := p.notify() 84 switch err { 85 case errNotEnoughTimePassed: 86 logger.Debug("not enough time passed") 87 case nil: 88 // skip 89 default: 90 logger.Error("error while sending a contact code", zap.Error(err)) 91 } 92 case <-p.quit: 93 ticker.Stop() 94 return 95 } 96 } 97 }() 98 } 99 100 func (p *Publisher) notify() error { 101 lastPublished := p.persistence.getLastPublished() 102 103 now := time.Now().Unix() 104 105 if now-lastPublished < publishInterval { 106 return errNotEnoughTimePassed 107 } 108 109 select { 110 case p.notifyCh <- struct{}{}: 111 default: 112 p.logger.Warn("publisher channel full, dropping message") 113 } 114 115 p.persistence.setLastPublished(now) 116 return nil 117 } 118 119 func (p *Publisher) ShouldAdvertiseBundle(publicKey *ecdsa.PublicKey, now int64) (bool, error) { 120 identity := crypto.CompressPubkey(publicKey) 121 lastAcked := p.persistence.lastAck(identity) 122 return now-lastAcked < deviceNotFoundAckInterval, nil 123 } 124 125 func (p *Publisher) SetLastAck(publicKey *ecdsa.PublicKey, now int64) { 126 identity := crypto.CompressPubkey(publicKey) 127 p.persistence.setLastAck(identity, now) 128 }