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  }