github.com/status-im/status-go@v1.1.0/protocol/transport/filters_manager.go (about)

     1  package transport
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"crypto/ecdsa"
     7  	"encoding/hex"
     8  	"sync"
     9  
    10  	"github.com/pkg/errors"
    11  	"go.uber.org/zap"
    12  
    13  	"github.com/status-im/status-go/eth-node/types"
    14  	"github.com/status-im/status-go/protocol/common/shard"
    15  )
    16  
    17  const (
    18  	minPow = 0.0
    19  )
    20  
    21  type RawFilter struct {
    22  	FilterID string
    23  	Topic    types.TopicType
    24  	SymKeyID string
    25  }
    26  
    27  type KeysPersistence interface {
    28  	All() (map[string][]byte, error)
    29  	Add(chatID string, key []byte) error
    30  }
    31  
    32  type FiltersService interface {
    33  	AddKeyPair(key *ecdsa.PrivateKey) (string, error)
    34  	DeleteKeyPair(keyID string) bool
    35  
    36  	AddSymKeyDirect(key []byte) (string, error)
    37  	AddSymKeyFromPassword(password string) (string, error)
    38  	GetSymKey(id string) ([]byte, error)
    39  	DeleteSymKey(id string) bool
    40  
    41  	Subscribe(opts *types.SubscriptionOptions) (string, error)
    42  	Unsubscribe(ctx context.Context, id string) error
    43  	UnsubscribeMany(ids []string) error
    44  }
    45  
    46  type FiltersManager struct {
    47  	service     FiltersService
    48  	persistence KeysPersistence
    49  	privateKey  *ecdsa.PrivateKey
    50  	keys        map[string][]byte // a cache of symmetric manager derived from passwords
    51  	logger      *zap.Logger
    52  	mutex       sync.Mutex
    53  	filters     map[string]*Filter
    54  }
    55  
    56  // NewFiltersManager returns a new filtersManager.
    57  func NewFiltersManager(persistence KeysPersistence, service FiltersService, privateKey *ecdsa.PrivateKey, logger *zap.Logger) (*FiltersManager, error) {
    58  	if logger == nil {
    59  		logger = zap.NewNop()
    60  	}
    61  
    62  	keys, err := persistence.All()
    63  	if err != nil {
    64  		return nil, err
    65  	}
    66  
    67  	return &FiltersManager{
    68  		privateKey:  privateKey,
    69  		service:     service,
    70  		persistence: persistence,
    71  		keys:        keys,
    72  		filters:     make(map[string]*Filter),
    73  		logger:      logger.With(zap.Namespace("filtersManager")),
    74  	}, nil
    75  }
    76  
    77  func (f *FiltersManager) Init(
    78  	filtersToInit []FiltersToInitialize,
    79  	publicKeys []*ecdsa.PublicKey,
    80  ) ([]*Filter, error) {
    81  
    82  	// Load our contact code.
    83  	_, err := f.LoadContactCode(&f.privateKey.PublicKey)
    84  	if err != nil {
    85  		return nil, errors.Wrap(err, "failed to load contact code")
    86  	}
    87  
    88  	// Load partitioned topic.
    89  	_, err = f.loadMyPartitioned()
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	// Add discovery topic.
    95  	_, err = f.LoadDiscovery()
    96  	if err != nil {
    97  		return nil, err
    98  	}
    99  
   100  	// Add public, one-to-one and negotiated filters.
   101  	for _, fi := range filtersToInit {
   102  		_, err := f.LoadPublic(fi.ChatID, fi.PubsubTopic)
   103  		if err != nil {
   104  			return nil, err
   105  		}
   106  	}
   107  
   108  	for _, publicKey := range publicKeys {
   109  		_, err := f.LoadContactCode(publicKey)
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  	}
   114  
   115  	f.mutex.Lock()
   116  	defer f.mutex.Unlock()
   117  
   118  	var allFilters []*Filter
   119  	for _, f := range f.filters {
   120  		allFilters = append(allFilters, f)
   121  	}
   122  	return allFilters, nil
   123  }
   124  
   125  type FiltersToInitialize struct {
   126  	ChatID      string
   127  	PubsubTopic string
   128  }
   129  
   130  func (f *FiltersManager) InitPublicFilters(publicFiltersToInit []FiltersToInitialize) ([]*Filter, error) {
   131  	var filters []*Filter
   132  	// Add public, one-to-one and negotiated filters.
   133  	for _, pf := range publicFiltersToInit {
   134  		f, err := f.LoadPublic(pf.ChatID, pf.PubsubTopic)
   135  		if err != nil {
   136  			return nil, err
   137  		}
   138  		filters = append(filters, f)
   139  	}
   140  	return filters, nil
   141  }
   142  
   143  type CommunityFilterToInitialize struct {
   144  	Shard   *shard.Shard
   145  	PrivKey *ecdsa.PrivateKey
   146  }
   147  
   148  func (f *FiltersManager) InitCommunityFilters(communityFiltersToInitialize []CommunityFilterToInitialize) ([]*Filter, error) {
   149  	var filters []*Filter
   150  	f.mutex.Lock()
   151  	defer f.mutex.Unlock()
   152  
   153  	for _, communityFilter := range communityFiltersToInitialize {
   154  		// to satisfy gosec: C601 checks
   155  		cf := communityFilter
   156  		if cf.PrivKey == nil {
   157  			continue
   158  		}
   159  
   160  		topics := make([]string, 0)
   161  		topics = append(topics, shard.DefaultNonProtectedPubsubTopic())
   162  		topics = append(topics, communityFilter.Shard.PubsubTopic())
   163  
   164  		for _, pubsubTopic := range topics {
   165  			pk := &cf.PrivKey.PublicKey
   166  			identityStr := PublicKeyToStr(pk)
   167  			rawFilter, err := f.addAsymmetric(identityStr, pubsubTopic, cf.PrivKey, true)
   168  			if err != nil {
   169  				f.logger.Debug("could not register community filter", zap.Error(err))
   170  				return nil, err
   171  
   172  			}
   173  			filterID := identityStr + "-admin" + pubsubTopic
   174  			filter := &Filter{
   175  				ChatID:       filterID,
   176  				FilterID:     rawFilter.FilterID,
   177  				PubsubTopic:  pubsubTopic,
   178  				ContentTopic: rawFilter.Topic,
   179  				Identity:     identityStr,
   180  				Listen:       true,
   181  				OneToOne:     true,
   182  			}
   183  
   184  			f.filters[filterID] = filter
   185  
   186  			f.logger.Debug("registering filter for", zap.String("chatID", filterID), zap.String("type", "community"), zap.String("pubsubTopic", pubsubTopic), zap.String("contentTopic", rawFilter.Topic.String()))
   187  
   188  			filters = append(filters, filter)
   189  		}
   190  	}
   191  	return filters, nil
   192  }
   193  
   194  // DEPRECATED
   195  func (f *FiltersManager) InitWithFilters(filters []*Filter) ([]*Filter, error) {
   196  	var (
   197  		filtersToInit []FiltersToInitialize
   198  		publicKeys    []*ecdsa.PublicKey
   199  	)
   200  
   201  	for _, filter := range filters {
   202  		if filter.Identity != "" && filter.OneToOne {
   203  			publicKey, err := StrToPublicKey(filter.Identity)
   204  			if err != nil {
   205  				return nil, err
   206  			}
   207  			publicKeys = append(publicKeys, publicKey)
   208  		} else if filter.ChatID != "" {
   209  			filtersToInit = append(filtersToInit, FiltersToInitialize{ChatID: filter.ChatID, PubsubTopic: filter.PubsubTopic})
   210  		}
   211  	}
   212  
   213  	return f.Init(filtersToInit, publicKeys)
   214  }
   215  
   216  func (f *FiltersManager) Reset(ctx context.Context) error {
   217  	var filters []*Filter
   218  
   219  	f.mutex.Lock()
   220  	for _, f := range f.filters {
   221  		filters = append(filters, f)
   222  	}
   223  	f.mutex.Unlock()
   224  
   225  	return f.Remove(ctx, filters...)
   226  }
   227  
   228  func (f *FiltersManager) Filters() (result []*Filter) {
   229  	f.mutex.Lock()
   230  	defer f.mutex.Unlock()
   231  
   232  	for _, f := range f.filters {
   233  		result = append(result, f)
   234  	}
   235  
   236  	return
   237  }
   238  
   239  func (f *FiltersManager) Filter(chatID string) *Filter {
   240  	f.mutex.Lock()
   241  	defer f.mutex.Unlock()
   242  	return f.filters[chatID]
   243  }
   244  
   245  // FilterByFilterID returns a Filter with a given Whisper filter ID.
   246  func (f *FiltersManager) FilterByFilterID(filterID string) *Filter {
   247  	f.mutex.Lock()
   248  	defer f.mutex.Unlock()
   249  	for _, f := range f.filters {
   250  		if f.FilterID == filterID {
   251  			return f
   252  		}
   253  	}
   254  	return nil
   255  }
   256  
   257  func (f *FiltersManager) FilterByTopic(topic []byte) *Filter {
   258  	f.mutex.Lock()
   259  	defer f.mutex.Unlock()
   260  	for _, f := range f.filters {
   261  		if bytes.Equal(types.TopicTypeToByteArray(f.ContentTopic), topic) {
   262  			return f
   263  		}
   264  	}
   265  	return nil
   266  }
   267  
   268  // FiltersByIdentities returns an array of filters for given list of public keys
   269  func (f *FiltersManager) FiltersByIdentities(identities []string) []*Filter {
   270  	f.mutex.Lock()
   271  	defer f.mutex.Unlock()
   272  
   273  	identitiesMap := make(map[string]bool)
   274  
   275  	for _, identity := range identities {
   276  		identitiesMap[identity] = true
   277  	}
   278  
   279  	var filters []*Filter
   280  
   281  	for _, filter := range f.filters {
   282  		// Pre-pend 0x before comparing
   283  		if identitiesMap["0x"+filter.Identity] {
   284  			filters = append(filters, filter)
   285  		}
   286  	}
   287  	return filters
   288  }
   289  
   290  // FilterByChatID returns a Filter for given chat id
   291  func (f *FiltersManager) FilterByChatID(chatID string) *Filter {
   292  	f.mutex.Lock()
   293  	defer f.mutex.Unlock()
   294  
   295  	return f.filters[chatID]
   296  }
   297  
   298  // Remove remove all the filters associated with a chat/identity
   299  func (f *FiltersManager) Remove(ctx context.Context, filters ...*Filter) error {
   300  	f.mutex.Lock()
   301  	defer f.mutex.Unlock()
   302  
   303  	for _, filter := range filters {
   304  		if err := f.service.Unsubscribe(ctx, filter.FilterID); err != nil {
   305  			return err
   306  		}
   307  		if filter.SymKeyID != "" {
   308  			f.service.DeleteSymKey(filter.SymKeyID)
   309  		}
   310  		delete(f.filters, filter.ChatID)
   311  	}
   312  
   313  	return nil
   314  }
   315  
   316  // Remove remove all the filters associated with a chat/identity
   317  func (f *FiltersManager) RemoveNoListenFilters() error {
   318  	f.mutex.Lock()
   319  	defer f.mutex.Unlock()
   320  	var filterIDs []string
   321  	var filters []*Filter
   322  
   323  	for _, f := range filters {
   324  		if !f.Listen {
   325  			filterIDs = append(filterIDs, f.FilterID)
   326  			filters = append(filters, f)
   327  		}
   328  	}
   329  	if err := f.service.UnsubscribeMany(filterIDs); err != nil {
   330  		return err
   331  	}
   332  
   333  	for _, filter := range filters {
   334  		if filter.SymKeyID != "" {
   335  			f.service.DeleteSymKey(filter.SymKeyID)
   336  		}
   337  		delete(f.filters, filter.ChatID)
   338  	}
   339  
   340  	return nil
   341  }
   342  
   343  // Remove remove all the filters associated with a chat/identity
   344  func (f *FiltersManager) RemoveFilterByChatID(chatID string) (*Filter, error) {
   345  	// TODO: remove subscriptions from waku2 if required. Might need to be implemented in transport
   346  
   347  	f.mutex.Lock()
   348  	filter, ok := f.filters[chatID]
   349  	f.mutex.Unlock()
   350  
   351  	if !ok {
   352  		return nil, nil
   353  	}
   354  
   355  	err := f.Remove(context.Background(), filter)
   356  	if err != nil {
   357  		return nil, err
   358  	}
   359  
   360  	return filter, nil
   361  }
   362  
   363  // LoadPartitioned creates a filter for a partitioned topic.
   364  func (f *FiltersManager) LoadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
   365  	return f.loadPartitioned(publicKey, identity, listen, false)
   366  }
   367  
   368  // LoadEphemeral creates a filter for a partitioned/personal topic.
   369  func (f *FiltersManager) LoadEphemeral(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
   370  	return f.loadPartitioned(publicKey, identity, listen, true)
   371  }
   372  
   373  // LoadPersonal creates a filter for a personal topic.
   374  func (f *FiltersManager) LoadPersonal(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen bool) (*Filter, error) {
   375  	f.mutex.Lock()
   376  	defer f.mutex.Unlock()
   377  
   378  	chatID := PersonalDiscoveryTopic(publicKey)
   379  	if _, ok := f.filters[chatID]; ok {
   380  		return f.filters[chatID], nil
   381  	}
   382  
   383  	// We set up a filter so we can publish,
   384  	// but we discard envelopes if listen is false.
   385  	filter, err := f.addAsymmetric(chatID, "", identity, listen)
   386  	if err != nil {
   387  		f.logger.Debug("could not register personal topic filter", zap.Error(err))
   388  		return nil, err
   389  	}
   390  
   391  	chat := &Filter{
   392  		ChatID:       chatID,
   393  		FilterID:     filter.FilterID,
   394  		ContentTopic: filter.Topic,
   395  		Identity:     PublicKeyToStr(publicKey),
   396  		Listen:       listen,
   397  		OneToOne:     true,
   398  	}
   399  
   400  	f.filters[chatID] = chat
   401  
   402  	f.logger.Debug("registering filter for", zap.String("chatID", chatID), zap.String("type", "personal"), zap.String("topic", filter.Topic.String()))
   403  
   404  	return chat, nil
   405  
   406  }
   407  
   408  func (f *FiltersManager) loadMyPartitioned() (*Filter, error) {
   409  	return f.loadPartitioned(&f.privateKey.PublicKey, f.privateKey, true, false)
   410  }
   411  
   412  func (f *FiltersManager) loadPartitioned(publicKey *ecdsa.PublicKey, identity *ecdsa.PrivateKey, listen, ephemeral bool) (*Filter, error) {
   413  	f.mutex.Lock()
   414  	defer f.mutex.Unlock()
   415  
   416  	chatID := PartitionedTopic(publicKey)
   417  	if _, ok := f.filters[chatID]; ok {
   418  		return f.filters[chatID], nil
   419  	}
   420  
   421  	// We set up a filter so we can publish,
   422  	// but we discard envelopes if listen is false.
   423  	filter, err := f.addAsymmetric(chatID, "", identity, listen)
   424  	if err != nil {
   425  		f.logger.Debug("could not register partitioned topic", zap.String("chatID", chatID), zap.Error(err))
   426  		return nil, err
   427  	}
   428  
   429  	chat := &Filter{
   430  		ChatID:       chatID,
   431  		FilterID:     filter.FilterID,
   432  		ContentTopic: filter.Topic,
   433  		Identity:     PublicKeyToStr(publicKey),
   434  		Listen:       listen,
   435  		Ephemeral:    ephemeral,
   436  		OneToOne:     true,
   437  	}
   438  
   439  	f.filters[chatID] = chat
   440  
   441  	f.logger.Debug("registering filter for", zap.String("chatID", chatID), zap.String("type", "partitioned"), zap.String("topic", filter.Topic.String()))
   442  
   443  	return chat, nil
   444  }
   445  
   446  // LoadNegotiated loads a negotiated secret as a filter.
   447  func (f *FiltersManager) LoadNegotiated(secret types.NegotiatedSecret) (*Filter, error) {
   448  	f.mutex.Lock()
   449  	defer f.mutex.Unlock()
   450  
   451  	chatID := NegotiatedTopic(secret.PublicKey)
   452  
   453  	if _, ok := f.filters[chatID]; ok {
   454  		return f.filters[chatID], nil
   455  	}
   456  
   457  	keyString := hex.EncodeToString(secret.Key)
   458  	filter, err := f.addSymmetric(keyString, "")
   459  	if err != nil {
   460  		f.logger.Debug("could not register negotiated topic", zap.Error(err))
   461  		return nil, err
   462  	}
   463  
   464  	chat := &Filter{
   465  		ChatID:       chatID,
   466  		ContentTopic: filter.Topic,
   467  		SymKeyID:     filter.SymKeyID,
   468  		FilterID:     filter.FilterID,
   469  		Identity:     PublicKeyToStr(secret.PublicKey),
   470  		Negotiated:   true,
   471  		Listen:       true,
   472  		OneToOne:     true,
   473  	}
   474  
   475  	f.filters[chat.ChatID] = chat
   476  
   477  	f.logger.Debug("registering filter for", zap.String("chatID", chatID), zap.String("type", "negotiated"), zap.String("topic", filter.Topic.String()))
   478  
   479  	return chat, nil
   480  }
   481  
   482  // LoadDiscovery adds 1 discovery filter
   483  // for the personal discovery topic.
   484  func (f *FiltersManager) LoadDiscovery() ([]*Filter, error) {
   485  	f.mutex.Lock()
   486  	defer f.mutex.Unlock()
   487  
   488  	personalDiscoveryTopic := PersonalDiscoveryTopic(&f.privateKey.PublicKey)
   489  
   490  	// Check if filters are already loaded.
   491  	var result []*Filter
   492  
   493  	expectedTopicCount := 1
   494  
   495  	if chat, ok := f.filters[personalDiscoveryTopic]; ok {
   496  		result = append(result, chat)
   497  	}
   498  
   499  	if len(result) == expectedTopicCount {
   500  		return result, nil
   501  	}
   502  
   503  	identityStr := PublicKeyToStr(&f.privateKey.PublicKey)
   504  
   505  	// Load personal discovery
   506  	personalDiscoveryChat := &Filter{
   507  		ChatID:    personalDiscoveryTopic,
   508  		Identity:  identityStr,
   509  		Discovery: true,
   510  		Listen:    true,
   511  		OneToOne:  true,
   512  	}
   513  
   514  	discoveryResponse, err := f.addAsymmetric(personalDiscoveryChat.ChatID, personalDiscoveryChat.PubsubTopic, f.privateKey, true)
   515  	if err != nil {
   516  		f.logger.Debug("could not register discovery topic", zap.String("chatID", personalDiscoveryChat.ChatID), zap.Error(err))
   517  		return nil, err
   518  	}
   519  
   520  	personalDiscoveryChat.ContentTopic = discoveryResponse.Topic
   521  	personalDiscoveryChat.FilterID = discoveryResponse.FilterID
   522  
   523  	f.filters[personalDiscoveryChat.ChatID] = personalDiscoveryChat
   524  
   525  	f.logger.Debug("registering filter for", zap.String("chatID", personalDiscoveryChat.ChatID), zap.String("type", "discovery"), zap.String("topic", personalDiscoveryChat.ContentTopic.String()))
   526  
   527  	return []*Filter{personalDiscoveryChat}, nil
   528  }
   529  
   530  func (f *FiltersManager) PersonalTopicFilter() *Filter {
   531  	personalDiscoveryTopic := PersonalDiscoveryTopic(&f.privateKey.PublicKey)
   532  
   533  	return f.filters[personalDiscoveryTopic]
   534  }
   535  
   536  // LoadPublic adds a filter for a public chat.
   537  func (f *FiltersManager) LoadPublic(chatID string, pubsubTopic string) (*Filter, error) {
   538  	f.mutex.Lock()
   539  	defer f.mutex.Unlock()
   540  
   541  	if chat, ok := f.filters[chatID]; ok {
   542  		if chat.PubsubTopic != pubsubTopic {
   543  			f.logger.Debug("updating pubsub topic for filter",
   544  				zap.String("chatID", chatID),
   545  				zap.String("type", "public"),
   546  				zap.String("oldTopic", chat.PubsubTopic),
   547  				zap.String("newTopic", pubsubTopic),
   548  			)
   549  			chat.PubsubTopic = pubsubTopic
   550  			f.filters[chatID] = chat
   551  		}
   552  
   553  		return chat, nil
   554  	}
   555  
   556  	filterAndTopic, err := f.addSymmetric(chatID, pubsubTopic)
   557  	if err != nil {
   558  		f.logger.Debug("could not register public chat topic", zap.String("chatID", chatID), zap.Error(err))
   559  		return nil, err
   560  	}
   561  
   562  	chat := &Filter{
   563  		ChatID:       chatID,
   564  		FilterID:     filterAndTopic.FilterID,
   565  		SymKeyID:     filterAndTopic.SymKeyID,
   566  		ContentTopic: filterAndTopic.Topic,
   567  		PubsubTopic:  pubsubTopic,
   568  		Listen:       true,
   569  		OneToOne:     false,
   570  	}
   571  
   572  	f.filters[chatID] = chat
   573  
   574  	f.logger.Debug("registering filter for",
   575  		zap.String("chatID", chatID),
   576  		zap.String("type", "public"),
   577  		zap.String("ContentTopic", filterAndTopic.Topic.String()),
   578  		zap.String("PubsubTopic", pubsubTopic),
   579  	)
   580  
   581  	return chat, nil
   582  }
   583  
   584  // LoadContactCode creates a filter for the advertise topic for a given public key.
   585  func (f *FiltersManager) LoadContactCode(pubKey *ecdsa.PublicKey) (*Filter, error) {
   586  	f.mutex.Lock()
   587  	defer f.mutex.Unlock()
   588  
   589  	chatID := ContactCodeTopic(pubKey)
   590  
   591  	if _, ok := f.filters[chatID]; ok {
   592  		return f.filters[chatID], nil
   593  	}
   594  
   595  	contactCodeFilter, err := f.addSymmetric(chatID, "")
   596  	if err != nil {
   597  		f.logger.Debug("could not register contact code topic", zap.String("chatID", chatID), zap.Error(err))
   598  		return nil, err
   599  	}
   600  
   601  	chat := &Filter{
   602  		ChatID:       chatID,
   603  		FilterID:     contactCodeFilter.FilterID,
   604  		ContentTopic: contactCodeFilter.Topic,
   605  		SymKeyID:     contactCodeFilter.SymKeyID,
   606  		Identity:     PublicKeyToStr(pubKey),
   607  		Listen:       true,
   608  	}
   609  
   610  	f.filters[chatID] = chat
   611  
   612  	f.logger.Debug("registering filter for", zap.String("chatID", chatID), zap.String("type", "contact-code"), zap.String("topic", contactCodeFilter.Topic.String()))
   613  
   614  	return chat, nil
   615  }
   616  
   617  // addSymmetric adds a symmetric key filter
   618  func (f *FiltersManager) addSymmetric(chatID string, pubsubTopic string) (*RawFilter, error) {
   619  	var symKeyID string
   620  	var err error
   621  
   622  	topic := ToTopic(chatID)
   623  	topics := [][]byte{topic}
   624  
   625  	symKey, ok := f.keys[chatID]
   626  	if ok {
   627  		symKeyID, err = f.service.AddSymKeyDirect(symKey)
   628  		if err != nil {
   629  			return nil, err
   630  		}
   631  	} else {
   632  		symKeyID, err = f.service.AddSymKeyFromPassword(chatID)
   633  		if err != nil {
   634  			return nil, err
   635  		}
   636  		if symKey, err = f.service.GetSymKey(symKeyID); err != nil {
   637  			return nil, err
   638  		}
   639  		f.keys[chatID] = symKey
   640  
   641  		err = f.persistence.Add(chatID, symKey)
   642  		if err != nil {
   643  			return nil, err
   644  		}
   645  	}
   646  
   647  	id, err := f.service.Subscribe(&types.SubscriptionOptions{
   648  		SymKeyID:    symKeyID,
   649  		PoW:         minPow,
   650  		Topics:      topics,
   651  		PubsubTopic: pubsubTopic,
   652  	})
   653  	if err != nil {
   654  		return nil, err
   655  	}
   656  
   657  	return &RawFilter{
   658  		FilterID: id,
   659  		SymKeyID: symKeyID,
   660  		Topic:    types.BytesToTopic(topic),
   661  	}, nil
   662  }
   663  
   664  // addAsymmetricFilter adds a filter with our private key
   665  // and set minPow according to the listen parameter.
   666  func (f *FiltersManager) addAsymmetric(chatID string, pubsubTopic string, identity *ecdsa.PrivateKey, listen bool) (*RawFilter, error) {
   667  	var (
   668  		err error
   669  		pow = 1.0 // use PoW high enough to discard all messages for the filter
   670  	)
   671  
   672  	if listen {
   673  		pow = minPow
   674  	}
   675  
   676  	topic := ToTopic(chatID)
   677  	topics := [][]byte{topic}
   678  
   679  	privateKeyID, err := f.service.AddKeyPair(identity)
   680  	if err != nil {
   681  		return nil, err
   682  	}
   683  
   684  	id, err := f.service.Subscribe(&types.SubscriptionOptions{
   685  		PrivateKeyID: privateKeyID,
   686  		PoW:          pow,
   687  		Topics:       topics,
   688  		PubsubTopic:  pubsubTopic,
   689  	})
   690  	if err != nil {
   691  		return nil, err
   692  	}
   693  	return &RawFilter{FilterID: id, Topic: types.BytesToTopic(topic)}, nil
   694  }
   695  
   696  // GetNegotiated returns a negotiated chat given an identity
   697  func (f *FiltersManager) GetNegotiated(identity *ecdsa.PublicKey) *Filter {
   698  	f.mutex.Lock()
   699  	defer f.mutex.Unlock()
   700  
   701  	return f.filters[NegotiatedTopic(identity)]
   702  }