github.com/status-im/status-go@v1.1.0/waku/common/filter_test.go (about)

     1  // Copyright 2019 The Waku Library Authors.
     2  //
     3  // The Waku library is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Lesser General Public License as published by
     5  // the Free Software Foundation, either version 3 of the License, or
     6  // (at your option) any later version.
     7  //
     8  // The Waku library is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty off
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    11  // GNU Lesser General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Lesser General Public License
    14  // along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
    15  //
    16  // This software uses the go-ethereum library, which is licensed
    17  // under the GNU Lesser General Public Library, version 3 or any later.
    18  
    19  package common
    20  
    21  import (
    22  	"math/big"
    23  	mrand "math/rand"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/crypto"
    29  )
    30  
    31  var seed int64
    32  
    33  // InitSingleTest should be called in the beginning of every
    34  // test, which uses RNG, in order to make the tests
    35  // reproduciblity independent of their sequence.
    36  func InitSingleTest() {
    37  	seed = time.Now().Unix()
    38  	mrand.Seed(seed)
    39  }
    40  
    41  type FilterTestCase struct {
    42  	f      *Filter
    43  	id     string
    44  	alive  bool
    45  	msgCnt int
    46  }
    47  
    48  func generateFilter(t *testing.T, symmetric bool) (*Filter, error) {
    49  	var f Filter
    50  	f.Messages = NewMemoryMessageStore()
    51  
    52  	const topicNum = 8
    53  	f.Topics = make([][]byte, topicNum)
    54  	for i := 0; i < topicNum; i++ {
    55  		f.Topics[i] = make([]byte, 4)
    56  		mrand.Read(f.Topics[i]) // nolint: gosec
    57  		f.Topics[i][0] = 0x01
    58  	}
    59  
    60  	key, err := crypto.GenerateKey()
    61  	if err != nil {
    62  		t.Fatalf("generateFilter 1 failed with seed %d.", seed)
    63  		return nil, err
    64  	}
    65  	f.Src = &key.PublicKey
    66  
    67  	if symmetric {
    68  		f.KeySym = make([]byte, AESKeyLength)
    69  		mrand.Read(f.KeySym) // nolint: gosec
    70  		f.SymKeyHash = crypto.Keccak256Hash(f.KeySym)
    71  	} else {
    72  		f.KeyAsym, err = crypto.GenerateKey()
    73  		if err != nil {
    74  			t.Fatalf("generateFilter 2 failed with seed %d.", seed)
    75  			return nil, err
    76  		}
    77  	}
    78  
    79  	// AcceptP2P & PoW are not set
    80  	return &f, nil
    81  }
    82  
    83  func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase {
    84  	cases := make([]FilterTestCase, SizeTestFilters)
    85  	for i := 0; i < SizeTestFilters; i++ {
    86  		f, _ := generateFilter(t, true)
    87  		cases[i].f = f
    88  		cases[i].alive = mrand.Int()&1 == 0 // nolint: gosec
    89  	}
    90  	return cases
    91  }
    92  
    93  func TestInstallFilters(t *testing.T) {
    94  	InitSingleTest()
    95  
    96  	const SizeTestFilters = 256
    97  	filters := NewFilters()
    98  	tst := generateTestCases(t, SizeTestFilters)
    99  
   100  	var err error
   101  	var j string
   102  	for i := 0; i < SizeTestFilters; i++ {
   103  		j, err = filters.Install(tst[i].f)
   104  		if err != nil {
   105  			t.Fatalf("seed %d: failed to install filter: %s", seed, err)
   106  		}
   107  		tst[i].id = j
   108  		if len(j) != KeyIDSize*2 {
   109  			t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
   110  		}
   111  	}
   112  
   113  	for _, testCase := range tst {
   114  		if !testCase.alive {
   115  			filters.Uninstall(testCase.id)
   116  		}
   117  	}
   118  
   119  	for i, testCase := range tst {
   120  		fil := filters.Get(testCase.id)
   121  		exist := fil != nil
   122  		if exist != testCase.alive {
   123  			t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive)
   124  		}
   125  		if exist && fil.PoW != testCase.f.PoW {
   126  			t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive)
   127  		}
   128  	}
   129  }
   130  
   131  func TestInstallSymKeyGeneratesHash(t *testing.T) {
   132  	InitSingleTest()
   133  
   134  	filters := NewFilters()
   135  	filter, _ := generateFilter(t, true)
   136  
   137  	// save the current SymKeyHash for comparison
   138  	initialSymKeyHash := filter.SymKeyHash
   139  
   140  	// ensure the SymKeyHash is invalid, for Install to recreate it
   141  	var invalid common.Hash
   142  	filter.SymKeyHash = invalid
   143  
   144  	_, err := filters.Install(filter)
   145  
   146  	if err != nil {
   147  		t.Fatalf("Error installing the filter: %s", err)
   148  	}
   149  
   150  	for i, b := range filter.SymKeyHash {
   151  		if b != initialSymKeyHash[i] {
   152  			t.Fatalf("The filter's symmetric key hash was not properly generated by Install")
   153  		}
   154  	}
   155  }
   156  
   157  func TestInstallIdenticalFilters(t *testing.T) {
   158  	InitSingleTest()
   159  
   160  	filters := NewFilters()
   161  	filter1, _ := generateFilter(t, true)
   162  
   163  	// Copy the first filter since some of its fields
   164  	// are randomly gnerated.
   165  	filter2 := &Filter{
   166  		KeySym:   filter1.KeySym,
   167  		Topics:   filter1.Topics,
   168  		PoW:      filter1.PoW,
   169  		AllowP2P: filter1.AllowP2P,
   170  		Messages: NewMemoryMessageStore(),
   171  	}
   172  
   173  	_, err := filters.Install(filter1)
   174  
   175  	if err != nil {
   176  		t.Fatalf("Error installing the first filter with seed %d: %s", seed, err)
   177  	}
   178  
   179  	_, err = filters.Install(filter2)
   180  
   181  	if err != nil {
   182  		t.Fatalf("Error installing the second filter with seed %d: %s", seed, err)
   183  	}
   184  
   185  	params, err := GenerateMessageParams()
   186  	if err != nil {
   187  		t.Fatalf("Error generating message parameters with seed %d: %s", seed, err)
   188  	}
   189  
   190  	params.KeySym = filter1.KeySym
   191  	params.Topic = BytesToTopic(filter1.Topics[0])
   192  
   193  	filter1.Src = &params.Src.PublicKey
   194  	filter2.Src = &params.Src.PublicKey
   195  
   196  	sentMessage, err := NewSentMessage(params)
   197  	if err != nil {
   198  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   199  	}
   200  	env, err := sentMessage.Wrap(params, time.Now())
   201  	if err != nil {
   202  		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
   203  	}
   204  	msg := env.Open(filter1)
   205  	if msg == nil {
   206  		t.Fatalf("failed to Open with filter1")
   207  	}
   208  
   209  	if !filter1.MatchEnvelope(env) {
   210  		t.Fatalf("failed matching with the first filter")
   211  	}
   212  
   213  	if !filter2.MatchEnvelope(env) {
   214  		t.Fatalf("failed matching with the first filter")
   215  	}
   216  
   217  	if !filter1.MatchMessage(msg) {
   218  		t.Fatalf("failed matching with the second filter")
   219  	}
   220  
   221  	if !filter2.MatchMessage(msg) {
   222  		t.Fatalf("failed matching with the second filter")
   223  	}
   224  }
   225  
   226  func TestInstallFilterWithSymAndAsymKeys(t *testing.T) {
   227  	InitSingleTest()
   228  
   229  	filters := NewFilters()
   230  	filter1, _ := generateFilter(t, true)
   231  
   232  	asymKey, err := crypto.GenerateKey()
   233  	if err != nil {
   234  		t.Fatalf("Unable to create asymetric keys: %v", err)
   235  	}
   236  
   237  	// Copy the first filter since some of its fields
   238  	// are randomly gnerated.
   239  	filter := &Filter{
   240  		KeySym:   filter1.KeySym,
   241  		KeyAsym:  asymKey,
   242  		Topics:   filter1.Topics,
   243  		PoW:      filter1.PoW,
   244  		AllowP2P: filter1.AllowP2P,
   245  		Messages: NewMemoryMessageStore(),
   246  	}
   247  
   248  	_, err = filters.Install(filter)
   249  
   250  	if err == nil {
   251  		t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed)
   252  	}
   253  }
   254  
   255  func TestComparePubKey(t *testing.T) {
   256  	InitSingleTest()
   257  
   258  	key1, err := crypto.GenerateKey()
   259  	if err != nil {
   260  		t.Fatalf("failed to generate first key with seed %d: %s.", seed, err)
   261  	}
   262  	key2, err := crypto.GenerateKey()
   263  	if err != nil {
   264  		t.Fatalf("failed to generate second key with seed %d: %s.", seed, err)
   265  	}
   266  	if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) {
   267  		t.Fatalf("public keys are equal, seed %d.", seed)
   268  	}
   269  
   270  	// generate key3 == key1
   271  	mrand.Seed(seed)
   272  	key3, err := crypto.GenerateKey()
   273  	if err != nil {
   274  		t.Fatalf("failed to generate third key with seed %d: %s.", seed, err)
   275  	}
   276  	if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) {
   277  		t.Fatalf("key1 == key3, seed %d.", seed)
   278  	}
   279  }
   280  
   281  func TestMatchEnvelope(t *testing.T) {
   282  	InitSingleTest()
   283  
   284  	fsym, err := generateFilter(t, true)
   285  	if err != nil {
   286  		t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
   287  	}
   288  
   289  	fasym, err := generateFilter(t, false)
   290  	if err != nil {
   291  		t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err)
   292  	}
   293  
   294  	params, err := GenerateMessageParams()
   295  	if err != nil {
   296  		t.Fatalf("failed GenerateMessageParams with seed %d: %s.", seed, err)
   297  	}
   298  
   299  	params.Topic[0] = 0xFF // topic mismatch
   300  
   301  	msg, err := NewSentMessage(params)
   302  	if err != nil {
   303  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   304  	}
   305  	_, err = msg.Wrap(params, time.Now())
   306  	if err != nil {
   307  		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
   308  	}
   309  
   310  	// encrypt symmetrically
   311  	i := mrand.Int() % 4 // nolint: gosec
   312  	fsym.Topics[i] = params.Topic[:]
   313  	fasym.Topics[i] = params.Topic[:]
   314  	msg, err = NewSentMessage(params)
   315  	if err != nil {
   316  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   317  	}
   318  	env, err := msg.Wrap(params, time.Now())
   319  	if err != nil {
   320  		t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
   321  	}
   322  
   323  	// symmetric + matching topic: match
   324  	match := fsym.MatchEnvelope(env)
   325  	if !match {
   326  		t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
   327  	}
   328  
   329  	// symmetric + matching topic + insufficient PoW: mismatch
   330  	fsym.PoW = env.PoW() + 1.0
   331  	match = fsym.MatchEnvelope(env)
   332  	if match {
   333  		t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed)
   334  	}
   335  
   336  	// symmetric + matching topic + sufficient PoW: match
   337  	fsym.PoW = env.PoW() / 2
   338  	match = fsym.MatchEnvelope(env)
   339  	if !match {
   340  		t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed)
   341  	}
   342  
   343  	// symmetric + topics are nil (wildcard): match
   344  	prevTopics := fsym.Topics
   345  	fsym.Topics = nil
   346  	match = fsym.MatchEnvelope(env)
   347  	if !match {
   348  		t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed)
   349  	}
   350  	fsym.Topics = prevTopics
   351  
   352  	// encrypt asymmetrically
   353  	key, err := crypto.GenerateKey()
   354  	if err != nil {
   355  		t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
   356  	}
   357  	params.KeySym = nil
   358  	params.Dst = &key.PublicKey
   359  	msg, err = NewSentMessage(params)
   360  	if err != nil {
   361  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   362  	}
   363  	env, err = msg.Wrap(params, time.Now())
   364  	if err != nil {
   365  		t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
   366  	}
   367  
   368  	// encryption method mismatch
   369  	match = fsym.MatchEnvelope(env)
   370  	if match {
   371  		t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
   372  	}
   373  
   374  	// asymmetric + mismatching topic: mismatch
   375  	match = fasym.MatchEnvelope(env)
   376  	if !match {
   377  		t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed)
   378  	}
   379  
   380  	// asymmetric + matching topic: match
   381  	fasym.Topics[i] = fasym.Topics[i+1]
   382  	match = fasym.MatchEnvelope(env)
   383  	if !match {
   384  		t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed)
   385  	}
   386  
   387  	// asymmetric + filter without topic (wildcard): match
   388  	fasym.Topics = nil
   389  	match = fasym.MatchEnvelope(env)
   390  	if !match {
   391  		t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed)
   392  	}
   393  
   394  	// asymmetric + insufficient PoW: mismatch
   395  	fasym.PoW = env.PoW() + 1.0
   396  	match = fasym.MatchEnvelope(env)
   397  	if match {
   398  		t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed)
   399  	}
   400  
   401  	// asymmetric + sufficient PoW: match
   402  	fasym.PoW = env.PoW() / 2
   403  	match = fasym.MatchEnvelope(env)
   404  	if !match {
   405  		t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed)
   406  	}
   407  
   408  	// filter without topic + envelope without topic: match
   409  	env.Topic = TopicType{}
   410  	match = fasym.MatchEnvelope(env)
   411  	if !match {
   412  		t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
   413  	}
   414  
   415  	// filter with topic + envelope without topic: mismatch
   416  	fasym.Topics = fsym.Topics
   417  	match = fasym.MatchEnvelope(env)
   418  	if !match {
   419  		// topic mismatch should have no affect, as topics are handled by topic matchers
   420  		t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
   421  	}
   422  }
   423  
   424  func TestMatchMessageSym(t *testing.T) {
   425  	InitSingleTest()
   426  
   427  	params, err := GenerateMessageParams()
   428  	if err != nil {
   429  		t.Fatalf("failed GenerateMessageParams with seed %d: %s.", seed, err)
   430  	}
   431  
   432  	f, err := generateFilter(t, true)
   433  	if err != nil {
   434  		t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
   435  	}
   436  
   437  	const index = 1
   438  	params.KeySym = f.KeySym
   439  	params.Topic = BytesToTopic(f.Topics[index])
   440  
   441  	sentMessage, err := NewSentMessage(params)
   442  	if err != nil {
   443  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   444  	}
   445  	env, err := sentMessage.Wrap(params, time.Now())
   446  	if err != nil {
   447  		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
   448  	}
   449  	msg := env.Open(f)
   450  	if msg == nil {
   451  		t.Fatalf("failed Open with seed %d.", seed)
   452  	}
   453  
   454  	// Src: match
   455  	*f.Src.X = *params.Src.PublicKey.X
   456  	*f.Src.Y = *params.Src.PublicKey.Y
   457  	if !f.MatchMessage(msg) {
   458  		t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed)
   459  	}
   460  
   461  	// insufficient PoW: mismatch
   462  	f.PoW = msg.PoW + 1.0
   463  	if f.MatchMessage(msg) {
   464  		t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
   465  	}
   466  
   467  	// sufficient PoW: match
   468  	f.PoW = msg.PoW / 2
   469  	if !f.MatchMessage(msg) {
   470  		t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
   471  	}
   472  
   473  	// topic mismatch
   474  	f.Topics[index][0]++
   475  	if !f.MatchMessage(msg) {
   476  		// topic mismatch should have no affect, as topics are handled by topic matchers
   477  		t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
   478  	}
   479  	f.Topics[index][0]--
   480  
   481  	// key mismatch
   482  	f.SymKeyHash[0]++
   483  	if f.MatchMessage(msg) {
   484  		t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
   485  	}
   486  	f.SymKeyHash[0]--
   487  
   488  	// Src absent: match
   489  	f.Src = nil
   490  	if !f.MatchMessage(msg) {
   491  		t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
   492  	}
   493  
   494  	// key hash mismatch
   495  	h := f.SymKeyHash
   496  	f.SymKeyHash = common.Hash{}
   497  	if f.MatchMessage(msg) {
   498  		t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed)
   499  	}
   500  	f.SymKeyHash = h
   501  	if !f.MatchMessage(msg) {
   502  		t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed)
   503  	}
   504  
   505  	// encryption method mismatch
   506  	f.KeySym = nil
   507  	f.KeyAsym, err = crypto.GenerateKey()
   508  	if err != nil {
   509  		t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
   510  	}
   511  	if f.MatchMessage(msg) {
   512  		t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
   513  	}
   514  }
   515  
   516  func TestMatchMessageAsym(t *testing.T) {
   517  	InitSingleTest()
   518  
   519  	f, err := generateFilter(t, false)
   520  	if err != nil {
   521  		t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
   522  	}
   523  
   524  	params, err := GenerateMessageParams()
   525  	if err != nil {
   526  		t.Fatalf("failed GenerateMessageParams with seed %d: %s.", seed, err)
   527  	}
   528  
   529  	const index = 1
   530  	params.Topic = BytesToTopic(f.Topics[index])
   531  	params.Dst = &f.KeyAsym.PublicKey
   532  	keySymOrig := params.KeySym
   533  	params.KeySym = nil
   534  
   535  	sentMessage, err := NewSentMessage(params)
   536  	if err != nil {
   537  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   538  	}
   539  	env, err := sentMessage.Wrap(params, time.Now())
   540  	if err != nil {
   541  		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
   542  	}
   543  	msg := env.Open(f)
   544  	if msg == nil {
   545  		t.Fatalf("failed to open with seed %d.", seed)
   546  	}
   547  
   548  	// Src: match
   549  	*f.Src.X = *params.Src.PublicKey.X
   550  	*f.Src.Y = *params.Src.PublicKey.Y
   551  	if !f.MatchMessage(msg) {
   552  		t.Fatalf("failed MatchMessage(src match) with seed %d.", seed)
   553  	}
   554  
   555  	// insufficient PoW: mismatch
   556  	f.PoW = msg.PoW + 1.0
   557  	if f.MatchMessage(msg) {
   558  		t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
   559  	}
   560  
   561  	// sufficient PoW: match
   562  	f.PoW = msg.PoW / 2
   563  	if !f.MatchMessage(msg) {
   564  		t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
   565  	}
   566  
   567  	// topic mismatch
   568  	f.Topics[index][0]++
   569  	if !f.MatchMessage(msg) {
   570  		// topic mismatch should have no affect, as topics are handled by topic matchers
   571  		t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
   572  	}
   573  	f.Topics[index][0]--
   574  
   575  	// key mismatch
   576  	prev := *f.KeyAsym.PublicKey.X
   577  	zero := *big.NewInt(0)
   578  	*f.KeyAsym.PublicKey.X = zero
   579  	if f.MatchMessage(msg) {
   580  		t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
   581  	}
   582  	*f.KeyAsym.PublicKey.X = prev
   583  
   584  	// Src absent: match
   585  	f.Src = nil
   586  	if !f.MatchMessage(msg) {
   587  		t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
   588  	}
   589  
   590  	// encryption method mismatch
   591  	f.KeySym = keySymOrig
   592  	f.KeyAsym = nil
   593  	if f.MatchMessage(msg) {
   594  		t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
   595  	}
   596  }
   597  
   598  func cloneFilter(orig *Filter) *Filter {
   599  	var clone Filter
   600  	clone.Messages = NewMemoryMessageStore()
   601  	clone.Src = orig.Src
   602  	clone.KeyAsym = orig.KeyAsym
   603  	clone.KeySym = orig.KeySym
   604  	clone.Topics = orig.Topics
   605  	clone.PoW = orig.PoW
   606  	clone.AllowP2P = orig.AllowP2P
   607  	clone.SymKeyHash = orig.SymKeyHash
   608  	return &clone
   609  }
   610  
   611  func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
   612  	params, err := GenerateMessageParams()
   613  	if err != nil {
   614  		t.Fatalf("failed GenerateMessageParams with seed %d: %s.", seed, err)
   615  		return nil
   616  	}
   617  
   618  	params.KeySym = f.KeySym
   619  	params.Topic = BytesToTopic(f.Topics[2])
   620  	sentMessage, err := NewSentMessage(params)
   621  	if err != nil {
   622  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   623  	}
   624  	env, err := sentMessage.Wrap(params, time.Now())
   625  	if err != nil {
   626  		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
   627  		return nil
   628  	}
   629  	return env
   630  }
   631  
   632  func TestWatchers(t *testing.T) {
   633  	InitSingleTest()
   634  
   635  	const NumFilters = 16
   636  	const NumMessages = 256
   637  	var i int
   638  	var j uint32
   639  	var e *Envelope
   640  	var x, firstID string
   641  	var err error
   642  
   643  	filters := NewFilters()
   644  	tst := generateTestCases(t, NumFilters)
   645  	for i = 0; i < NumFilters; i++ {
   646  		tst[i].f.Src = nil
   647  		x, err = filters.Install(tst[i].f)
   648  		if err != nil {
   649  			t.Fatalf("failed to install filter with seed %d: %s.", seed, err)
   650  		}
   651  		tst[i].id = x
   652  		if len(firstID) == 0 {
   653  			firstID = x
   654  		}
   655  	}
   656  
   657  	lastID := x
   658  
   659  	var envelopes [NumMessages]*Envelope
   660  	for i = 0; i < NumMessages; i++ {
   661  		j = mrand.Uint32() % NumFilters // nolint: gosec
   662  		e = generateCompatibeEnvelope(t, tst[j].f)
   663  		envelopes[i] = e
   664  		tst[j].msgCnt++
   665  	}
   666  
   667  	for i = 0; i < NumMessages; i++ {
   668  		filters.NotifyWatchers(envelopes[i], false)
   669  	}
   670  
   671  	var total int
   672  	var mail []*ReceivedMessage
   673  	var count [NumFilters]int
   674  
   675  	for i = 0; i < NumFilters; i++ {
   676  		mail = tst[i].f.Retrieve()
   677  		count[i] = len(mail)
   678  		total += len(mail)
   679  	}
   680  
   681  	if total != NumMessages {
   682  		t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages)
   683  	}
   684  
   685  	for i = 0; i < NumFilters; i++ {
   686  		mail = tst[i].f.Retrieve()
   687  		if len(mail) != 0 {
   688  			t.Fatalf("failed with seed %d: i = %d.", seed, i)
   689  		}
   690  
   691  		if tst[i].msgCnt != count[i] {
   692  			t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
   693  		}
   694  	}
   695  
   696  	// another round with a cloned filter
   697  
   698  	clone := cloneFilter(tst[0].f)
   699  	filters.Uninstall(lastID)
   700  	total = 0
   701  	last := NumFilters - 1
   702  	tst[last].f = clone
   703  	if _, err := filters.Install(clone); err != nil {
   704  		t.Fatal("failed to install filter")
   705  	}
   706  
   707  	for i = 0; i < NumFilters; i++ {
   708  		tst[i].msgCnt = 0
   709  		count[i] = 0
   710  	}
   711  
   712  	// make sure that the first watcher receives at least one message
   713  	e = generateCompatibeEnvelope(t, tst[0].f)
   714  	envelopes[0] = e
   715  	tst[0].msgCnt++
   716  	for i = 1; i < NumMessages; i++ {
   717  		j = mrand.Uint32() % NumFilters // nolint: gosec
   718  		e = generateCompatibeEnvelope(t, tst[j].f)
   719  		envelopes[i] = e
   720  		tst[j].msgCnt++
   721  	}
   722  
   723  	for i = 0; i < NumMessages; i++ {
   724  		filters.NotifyWatchers(envelopes[i], false)
   725  	}
   726  
   727  	for i = 0; i < NumFilters; i++ {
   728  		mail = tst[i].f.Retrieve()
   729  		count[i] = len(mail)
   730  		total += len(mail)
   731  	}
   732  
   733  	combined := tst[0].msgCnt + tst[last].msgCnt
   734  	if total != NumMessages+count[0] {
   735  		t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0])
   736  	}
   737  
   738  	if combined != count[0] {
   739  		t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0])
   740  	}
   741  
   742  	if combined != count[last] {
   743  		t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last])
   744  	}
   745  
   746  	for i = 1; i < NumFilters-1; i++ {
   747  		mail = tst[i].f.Retrieve()
   748  		if len(mail) != 0 {
   749  			t.Fatalf("failed with seed %d: i = %d.", seed, i)
   750  		}
   751  
   752  		if tst[i].msgCnt != count[i] {
   753  			t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
   754  		}
   755  	}
   756  
   757  	// test AcceptP2P
   758  
   759  	total = 0
   760  	filters.NotifyWatchers(envelopes[0], true)
   761  
   762  	for i = 0; i < NumFilters; i++ {
   763  		mail = tst[i].f.Retrieve()
   764  		total += len(mail)
   765  	}
   766  
   767  	if total != 0 {
   768  		t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total)
   769  	}
   770  
   771  	f := filters.Get(firstID)
   772  	if f == nil {
   773  		t.Fatalf("failed to get the filter with seed %d.", seed)
   774  	}
   775  	f.AllowP2P = true
   776  	total = 0
   777  	filters.NotifyWatchers(envelopes[0], true)
   778  
   779  	for i = 0; i < NumFilters; i++ {
   780  		mail = tst[i].f.Retrieve()
   781  		total += len(mail)
   782  	}
   783  
   784  	if total != 1 {
   785  		t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total)
   786  	}
   787  }
   788  
   789  func TestVariableTopics(t *testing.T) {
   790  	InitSingleTest()
   791  
   792  	const lastTopicByte = 3
   793  	var match bool
   794  	params, err := GenerateMessageParams()
   795  	if err != nil {
   796  		t.Fatalf("failed GenerateMessageParams with seed %d: %s.", seed, err)
   797  	}
   798  	msg, err := NewSentMessage(params)
   799  	if err != nil {
   800  		t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
   801  	}
   802  	env, err := msg.Wrap(params, time.Now())
   803  	if err != nil {
   804  		t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
   805  	}
   806  
   807  	f, err := generateFilter(t, true)
   808  	if err != nil {
   809  		t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
   810  	}
   811  
   812  	for i := 0; i < 4; i++ {
   813  		env.Topic = BytesToTopic(f.Topics[i])
   814  		match = f.MatchEnvelope(env)
   815  		if !match {
   816  			t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
   817  		}
   818  
   819  		f.Topics[i][lastTopicByte]++
   820  		match = f.MatchEnvelope(env)
   821  		if !match {
   822  			// topic mismatch should have no affect, as topics are handled by topic matchers
   823  			t.Fatalf("MatchEnvelope symmetric with seed %d, step %d.", seed, i)
   824  		}
   825  	}
   826  }