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