834 lines
21 KiB
Go
834 lines
21 KiB
Go
|
// Copyright 2019 The Waku Library Authors.
|
||
|
//
|
||
|
// The Waku library is free software: you can redistribute it and/or modify
|
||
|
// it under the terms of the GNU Lesser General Public License as published by
|
||
|
// the Free Software Foundation, either version 3 of the License, or
|
||
|
// (at your option) any later version.
|
||
|
//
|
||
|
// The Waku library is distributed in the hope that it will be useful,
|
||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty off
|
||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
// GNU Lesser General Public License for more details.
|
||
|
//
|
||
|
// You should have received a copy of the GNU Lesser General Public License
|
||
|
// along with the Waku library. If not, see <http://www.gnu.org/licenses/>.
|
||
|
//
|
||
|
// This software uses the go-ethereum library, which is licensed
|
||
|
// under the GNU Lesser General Public Library, version 3 or any later.
|
||
|
|
||
|
package waku
|
||
|
|
||
|
import (
|
||
|
"math/big"
|
||
|
mrand "math/rand"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ethereum/go-ethereum/common"
|
||
|
"github.com/ethereum/go-ethereum/crypto"
|
||
|
)
|
||
|
|
||
|
var seed int64
|
||
|
|
||
|
// InitSingleTest should be called in the beginning of every
|
||
|
// test, which uses RNG, in order to make the tests
|
||
|
// reproduciblity independent of their sequence.
|
||
|
func InitSingleTest() {
|
||
|
seed = time.Now().Unix()
|
||
|
mrand.Seed(seed)
|
||
|
}
|
||
|
|
||
|
func InitDebugTest(i int64) {
|
||
|
seed = i
|
||
|
mrand.Seed(seed)
|
||
|
}
|
||
|
|
||
|
type FilterTestCase struct {
|
||
|
f *Filter
|
||
|
id string
|
||
|
alive bool
|
||
|
msgCnt int
|
||
|
}
|
||
|
|
||
|
func generateFilter(t *testing.T, symmetric bool) (*Filter, error) {
|
||
|
var f Filter
|
||
|
f.Messages = NewMemoryMessageStore()
|
||
|
|
||
|
const topicNum = 8
|
||
|
f.Topics = make([][]byte, topicNum)
|
||
|
for i := 0; i < topicNum; i++ {
|
||
|
f.Topics[i] = make([]byte, 4)
|
||
|
mrand.Read(f.Topics[i])
|
||
|
f.Topics[i][0] = 0x01
|
||
|
}
|
||
|
|
||
|
key, err := crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("generateFilter 1 failed with seed %d.", seed)
|
||
|
return nil, err
|
||
|
}
|
||
|
f.Src = &key.PublicKey
|
||
|
|
||
|
if symmetric {
|
||
|
f.KeySym = make([]byte, aesKeyLength)
|
||
|
mrand.Read(f.KeySym)
|
||
|
f.SymKeyHash = crypto.Keccak256Hash(f.KeySym)
|
||
|
} else {
|
||
|
f.KeyAsym, err = crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("generateFilter 2 failed with seed %d.", seed)
|
||
|
return nil, err
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// AcceptP2P & PoW are not set
|
||
|
return &f, nil
|
||
|
}
|
||
|
|
||
|
func generateTestCases(t *testing.T, SizeTestFilters int) []FilterTestCase {
|
||
|
cases := make([]FilterTestCase, SizeTestFilters)
|
||
|
for i := 0; i < SizeTestFilters; i++ {
|
||
|
f, _ := generateFilter(t, true)
|
||
|
cases[i].f = f
|
||
|
cases[i].alive = mrand.Int()&int(1) == 0
|
||
|
}
|
||
|
return cases
|
||
|
}
|
||
|
|
||
|
func TestInstallFilters(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
const SizeTestFilters = 256
|
||
|
w := New(&Config{}, nil)
|
||
|
filters := NewFilters(w)
|
||
|
tst := generateTestCases(t, SizeTestFilters)
|
||
|
|
||
|
var err error
|
||
|
var j string
|
||
|
for i := 0; i < SizeTestFilters; i++ {
|
||
|
j, err = filters.Install(tst[i].f)
|
||
|
if err != nil {
|
||
|
t.Fatalf("seed %d: failed to install filter: %s", seed, err)
|
||
|
}
|
||
|
tst[i].id = j
|
||
|
if len(j) != keyIDSize*2 {
|
||
|
t.Fatalf("seed %d: wrong filter id size [%d]", seed, len(j))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for _, testCase := range tst {
|
||
|
if !testCase.alive {
|
||
|
filters.Uninstall(testCase.id)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for i, testCase := range tst {
|
||
|
fil := filters.Get(testCase.id)
|
||
|
exist := fil != nil
|
||
|
if exist != testCase.alive {
|
||
|
t.Fatalf("seed %d: failed alive: %d, %v, %v", seed, i, exist, testCase.alive)
|
||
|
}
|
||
|
if exist && fil.PoW != testCase.f.PoW {
|
||
|
t.Fatalf("seed %d: failed Get: %d, %v, %v", seed, i, exist, testCase.alive)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInstallSymKeyGeneratesHash(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
w := New(&Config{}, nil)
|
||
|
filters := NewFilters(w)
|
||
|
filter, _ := generateFilter(t, true)
|
||
|
|
||
|
// save the current SymKeyHash for comparison
|
||
|
initialSymKeyHash := filter.SymKeyHash
|
||
|
|
||
|
// ensure the SymKeyHash is invalid, for Install to recreate it
|
||
|
var invalid common.Hash
|
||
|
filter.SymKeyHash = invalid
|
||
|
|
||
|
_, err := filters.Install(filter)
|
||
|
|
||
|
if err != nil {
|
||
|
t.Fatalf("Error installing the filter: %s", err)
|
||
|
}
|
||
|
|
||
|
for i, b := range filter.SymKeyHash {
|
||
|
if b != initialSymKeyHash[i] {
|
||
|
t.Fatalf("The filter's symmetric key hash was not properly generated by Install")
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInstallIdenticalFilters(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
w := New(&Config{}, nil)
|
||
|
filters := NewFilters(w)
|
||
|
filter1, _ := generateFilter(t, true)
|
||
|
|
||
|
// Copy the first filter since some of its fields
|
||
|
// are randomly gnerated.
|
||
|
filter2 := &Filter{
|
||
|
KeySym: filter1.KeySym,
|
||
|
Topics: filter1.Topics,
|
||
|
PoW: filter1.PoW,
|
||
|
AllowP2P: filter1.AllowP2P,
|
||
|
Messages: NewMemoryMessageStore(),
|
||
|
}
|
||
|
|
||
|
_, err := filters.Install(filter1)
|
||
|
|
||
|
if err != nil {
|
||
|
t.Fatalf("Error installing the first filter with seed %d: %s", seed, err)
|
||
|
}
|
||
|
|
||
|
_, err = filters.Install(filter2)
|
||
|
|
||
|
if err != nil {
|
||
|
t.Fatalf("Error installing the second filter with seed %d: %s", seed, err)
|
||
|
}
|
||
|
|
||
|
params, err := generateMessageParams()
|
||
|
if err != nil {
|
||
|
t.Fatalf("Error generating message parameters with seed %d: %s", seed, err)
|
||
|
}
|
||
|
|
||
|
params.KeySym = filter1.KeySym
|
||
|
params.Topic = BytesToTopic(filter1.Topics[0])
|
||
|
|
||
|
filter1.Src = ¶ms.Src.PublicKey
|
||
|
filter2.Src = ¶ms.Src.PublicKey
|
||
|
|
||
|
sentMessage, err := NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err := sentMessage.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
msg := env.Open(filter1)
|
||
|
if msg == nil {
|
||
|
t.Fatalf("failed to Open with filter1")
|
||
|
}
|
||
|
|
||
|
if !filter1.MatchEnvelope(env) {
|
||
|
t.Fatalf("failed matching with the first filter")
|
||
|
}
|
||
|
|
||
|
if !filter2.MatchEnvelope(env) {
|
||
|
t.Fatalf("failed matching with the first filter")
|
||
|
}
|
||
|
|
||
|
if !filter1.MatchMessage(msg) {
|
||
|
t.Fatalf("failed matching with the second filter")
|
||
|
}
|
||
|
|
||
|
if !filter2.MatchMessage(msg) {
|
||
|
t.Fatalf("failed matching with the second filter")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestInstallFilterWithSymAndAsymKeys(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
w := New(&Config{}, nil)
|
||
|
filters := NewFilters(w)
|
||
|
filter1, _ := generateFilter(t, true)
|
||
|
|
||
|
asymKey, err := crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("Unable to create asymetric keys: %v", err)
|
||
|
}
|
||
|
|
||
|
// Copy the first filter since some of its fields
|
||
|
// are randomly gnerated.
|
||
|
filter := &Filter{
|
||
|
KeySym: filter1.KeySym,
|
||
|
KeyAsym: asymKey,
|
||
|
Topics: filter1.Topics,
|
||
|
PoW: filter1.PoW,
|
||
|
AllowP2P: filter1.AllowP2P,
|
||
|
Messages: NewMemoryMessageStore(),
|
||
|
}
|
||
|
|
||
|
_, err = filters.Install(filter)
|
||
|
|
||
|
if err == nil {
|
||
|
t.Fatalf("Error detecting that a filter had both an asymmetric and symmetric key, with seed %d", seed)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestComparePubKey(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
key1, err := crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to generate first key with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
key2, err := crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to generate second key with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
if IsPubKeyEqual(&key1.PublicKey, &key2.PublicKey) {
|
||
|
t.Fatalf("public keys are equal, seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// generate key3 == key1
|
||
|
mrand.Seed(seed)
|
||
|
key3, err := crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to generate third key with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
if IsPubKeyEqual(&key1.PublicKey, &key3.PublicKey) {
|
||
|
t.Fatalf("key1 == key3, seed %d.", seed)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMatchEnvelope(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
fsym, err := generateFilter(t, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
fasym, err := generateFilter(t, false)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateFilter() with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
params, err := generateMessageParams()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
params.Topic[0] = 0xFF // topic mismatch
|
||
|
|
||
|
msg, err := NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err := msg.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
// encrypt symmetrically
|
||
|
i := mrand.Int() % 4
|
||
|
fsym.Topics[i] = params.Topic[:]
|
||
|
fasym.Topics[i] = params.Topic[:]
|
||
|
msg, err = NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err = msg.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
// symmetric + matching topic: match
|
||
|
match := fsym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope() symmetric with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// symmetric + matching topic + insufficient PoW: mismatch
|
||
|
fsym.PoW = env.PoW() + 1.0
|
||
|
match = fsym.MatchEnvelope(env)
|
||
|
if match {
|
||
|
t.Fatalf("failed MatchEnvelope(symmetric + matching topic + insufficient PoW) asymmetric with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// symmetric + matching topic + sufficient PoW: match
|
||
|
fsym.PoW = env.PoW() / 2
|
||
|
match = fsym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(symmetric + matching topic + sufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// symmetric + topics are nil (wildcard): match
|
||
|
prevTopics := fsym.Topics
|
||
|
fsym.Topics = nil
|
||
|
match = fsym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(symmetric + topics are nil) with seed %d.", seed)
|
||
|
}
|
||
|
fsym.Topics = prevTopics
|
||
|
|
||
|
// encrypt asymmetrically
|
||
|
key, err := crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
params.KeySym = nil
|
||
|
params.Dst = &key.PublicKey
|
||
|
msg, err = NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err = msg.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap() with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
// encryption method mismatch
|
||
|
match = fsym.MatchEnvelope(env)
|
||
|
if match {
|
||
|
t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// asymmetric + mismatching topic: mismatch
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(asymmetric + mismatching topic) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// asymmetric + matching topic: match
|
||
|
fasym.Topics[i] = fasym.Topics[i+1]
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(asymmetric + matching topic) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// asymmetric + filter without topic (wildcard): match
|
||
|
fasym.Topics = nil
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(asymmetric + filter without topic) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// asymmetric + insufficient PoW: mismatch
|
||
|
fasym.PoW = env.PoW() + 1.0
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if match {
|
||
|
t.Fatalf("failed MatchEnvelope(asymmetric + insufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// asymmetric + sufficient PoW: match
|
||
|
fasym.PoW = env.PoW() / 2
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(asymmetric + sufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// filter without topic + envelope without topic: match
|
||
|
env.Topic = TopicType{}
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// filter with topic + envelope without topic: mismatch
|
||
|
fasym.Topics = fsym.Topics
|
||
|
match = fasym.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
// topic mismatch should have no affect, as topics are handled by topic matchers
|
||
|
t.Fatalf("failed MatchEnvelope(filter without topic + envelope without topic) with seed %d.", seed)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMatchMessageSym(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
params, err := generateMessageParams()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
f, err := generateFilter(t, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
const index = 1
|
||
|
params.KeySym = f.KeySym
|
||
|
params.Topic = BytesToTopic(f.Topics[index])
|
||
|
|
||
|
sentMessage, err := NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err := sentMessage.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
msg := env.Open(f)
|
||
|
if msg == nil {
|
||
|
t.Fatalf("failed Open with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// Src: match
|
||
|
*f.Src.X = *params.Src.PublicKey.X
|
||
|
*f.Src.Y = *params.Src.PublicKey.Y
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(src match) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// insufficient PoW: mismatch
|
||
|
f.PoW = msg.PoW + 1.0
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// sufficient PoW: match
|
||
|
f.PoW = msg.PoW / 2
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// topic mismatch
|
||
|
f.Topics[index][0]++
|
||
|
if !f.MatchMessage(msg) {
|
||
|
// topic mismatch should have no affect, as topics are handled by topic matchers
|
||
|
t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
f.Topics[index][0]--
|
||
|
|
||
|
// key mismatch
|
||
|
f.SymKeyHash[0]++
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
f.SymKeyHash[0]--
|
||
|
|
||
|
// Src absent: match
|
||
|
f.Src = nil
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// key hash mismatch
|
||
|
h := f.SymKeyHash
|
||
|
f.SymKeyHash = common.Hash{}
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(key hash mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
f.SymKeyHash = h
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(key hash match) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// encryption method mismatch
|
||
|
f.KeySym = nil
|
||
|
f.KeyAsym, err = crypto.GenerateKey()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed GenerateKey with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestMatchMessageAsym(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
f, err := generateFilter(t, false)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
params, err := generateMessageParams()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
const index = 1
|
||
|
params.Topic = BytesToTopic(f.Topics[index])
|
||
|
params.Dst = &f.KeyAsym.PublicKey
|
||
|
keySymOrig := params.KeySym
|
||
|
params.KeySym = nil
|
||
|
|
||
|
sentMessage, err := NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err := sentMessage.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
msg := env.Open(f)
|
||
|
if msg == nil {
|
||
|
t.Fatalf("failed to open with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// Src: match
|
||
|
*f.Src.X = *params.Src.PublicKey.X
|
||
|
*f.Src.Y = *params.Src.PublicKey.Y
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchMessage(src match) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// insufficient PoW: mismatch
|
||
|
f.PoW = msg.PoW + 1.0
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(insufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// sufficient PoW: match
|
||
|
f.PoW = msg.PoW / 2
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(sufficient PoW) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// topic mismatch
|
||
|
f.Topics[index][0]++
|
||
|
if !f.MatchMessage(msg) {
|
||
|
// topic mismatch should have no affect, as topics are handled by topic matchers
|
||
|
t.Fatalf("failed MatchEnvelope(topic mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
f.Topics[index][0]--
|
||
|
|
||
|
// key mismatch
|
||
|
prev := *f.KeyAsym.PublicKey.X
|
||
|
zero := *big.NewInt(0)
|
||
|
*f.KeyAsym.PublicKey.X = zero
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(key mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
*f.KeyAsym.PublicKey.X = prev
|
||
|
|
||
|
// Src absent: match
|
||
|
f.Src = nil
|
||
|
if !f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(src absent) with seed %d.", seed)
|
||
|
}
|
||
|
|
||
|
// encryption method mismatch
|
||
|
f.KeySym = keySymOrig
|
||
|
f.KeyAsym = nil
|
||
|
if f.MatchMessage(msg) {
|
||
|
t.Fatalf("failed MatchEnvelope(encryption method mismatch) with seed %d.", seed)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func cloneFilter(orig *Filter) *Filter {
|
||
|
var clone Filter
|
||
|
clone.Messages = NewMemoryMessageStore()
|
||
|
clone.Src = orig.Src
|
||
|
clone.KeyAsym = orig.KeyAsym
|
||
|
clone.KeySym = orig.KeySym
|
||
|
clone.Topics = orig.Topics
|
||
|
clone.PoW = orig.PoW
|
||
|
clone.AllowP2P = orig.AllowP2P
|
||
|
clone.SymKeyHash = orig.SymKeyHash
|
||
|
return &clone
|
||
|
}
|
||
|
|
||
|
func generateCompatibeEnvelope(t *testing.T, f *Filter) *Envelope {
|
||
|
params, err := generateMessageParams()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
params.KeySym = f.KeySym
|
||
|
params.Topic = BytesToTopic(f.Topics[2])
|
||
|
sentMessage, err := NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err := sentMessage.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||
|
return nil
|
||
|
}
|
||
|
return env
|
||
|
}
|
||
|
|
||
|
func TestWatchers(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
const NumFilters = 16
|
||
|
const NumMessages = 256
|
||
|
var i int
|
||
|
var j uint32
|
||
|
var e *Envelope
|
||
|
var x, firstID string
|
||
|
var err error
|
||
|
|
||
|
w := New(&Config{}, nil)
|
||
|
filters := NewFilters(w)
|
||
|
tst := generateTestCases(t, NumFilters)
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
tst[i].f.Src = nil
|
||
|
x, err = filters.Install(tst[i].f)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to install filter with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
tst[i].id = x
|
||
|
if len(firstID) == 0 {
|
||
|
firstID = x
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lastID := x
|
||
|
|
||
|
var envelopes [NumMessages]*Envelope
|
||
|
for i = 0; i < NumMessages; i++ {
|
||
|
j = mrand.Uint32() % NumFilters
|
||
|
e = generateCompatibeEnvelope(t, tst[j].f)
|
||
|
envelopes[i] = e
|
||
|
tst[j].msgCnt++
|
||
|
}
|
||
|
|
||
|
for i = 0; i < NumMessages; i++ {
|
||
|
filters.NotifyWatchers(envelopes[i], false)
|
||
|
}
|
||
|
|
||
|
var total int
|
||
|
var mail []*ReceivedMessage
|
||
|
var count [NumFilters]int
|
||
|
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
mail = tst[i].f.Retrieve()
|
||
|
count[i] = len(mail)
|
||
|
total += len(mail)
|
||
|
}
|
||
|
|
||
|
if total != NumMessages {
|
||
|
t.Fatalf("failed with seed %d: total = %d, want: %d.", seed, total, NumMessages)
|
||
|
}
|
||
|
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
mail = tst[i].f.Retrieve()
|
||
|
if len(mail) != 0 {
|
||
|
t.Fatalf("failed with seed %d: i = %d.", seed, i)
|
||
|
}
|
||
|
|
||
|
if tst[i].msgCnt != count[i] {
|
||
|
t.Fatalf("failed with seed %d: count[%d]: get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// another round with a cloned filter
|
||
|
|
||
|
clone := cloneFilter(tst[0].f)
|
||
|
filters.Uninstall(lastID)
|
||
|
total = 0
|
||
|
last := NumFilters - 1
|
||
|
tst[last].f = clone
|
||
|
filters.Install(clone)
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
tst[i].msgCnt = 0
|
||
|
count[i] = 0
|
||
|
}
|
||
|
|
||
|
// make sure that the first watcher receives at least one message
|
||
|
e = generateCompatibeEnvelope(t, tst[0].f)
|
||
|
envelopes[0] = e
|
||
|
tst[0].msgCnt++
|
||
|
for i = 1; i < NumMessages; i++ {
|
||
|
j = mrand.Uint32() % NumFilters
|
||
|
e = generateCompatibeEnvelope(t, tst[j].f)
|
||
|
envelopes[i] = e
|
||
|
tst[j].msgCnt++
|
||
|
}
|
||
|
|
||
|
for i = 0; i < NumMessages; i++ {
|
||
|
filters.NotifyWatchers(envelopes[i], false)
|
||
|
}
|
||
|
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
mail = tst[i].f.Retrieve()
|
||
|
count[i] = len(mail)
|
||
|
total += len(mail)
|
||
|
}
|
||
|
|
||
|
combined := tst[0].msgCnt + tst[last].msgCnt
|
||
|
if total != NumMessages+count[0] {
|
||
|
t.Fatalf("failed with seed %d: total = %d, count[0] = %d.", seed, total, count[0])
|
||
|
}
|
||
|
|
||
|
if combined != count[0] {
|
||
|
t.Fatalf("failed with seed %d: combined = %d, count[0] = %d.", seed, combined, count[0])
|
||
|
}
|
||
|
|
||
|
if combined != count[last] {
|
||
|
t.Fatalf("failed with seed %d: combined = %d, count[last] = %d.", seed, combined, count[last])
|
||
|
}
|
||
|
|
||
|
for i = 1; i < NumFilters-1; i++ {
|
||
|
mail = tst[i].f.Retrieve()
|
||
|
if len(mail) != 0 {
|
||
|
t.Fatalf("failed with seed %d: i = %d.", seed, i)
|
||
|
}
|
||
|
|
||
|
if tst[i].msgCnt != count[i] {
|
||
|
t.Fatalf("failed with seed %d: i = %d, get %d, want %d.", seed, i, tst[i].msgCnt, count[i])
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// test AcceptP2P
|
||
|
|
||
|
total = 0
|
||
|
filters.NotifyWatchers(envelopes[0], true)
|
||
|
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
mail = tst[i].f.Retrieve()
|
||
|
total += len(mail)
|
||
|
}
|
||
|
|
||
|
if total != 0 {
|
||
|
t.Fatalf("failed with seed %d: total: got %d, want 0.", seed, total)
|
||
|
}
|
||
|
|
||
|
f := filters.Get(firstID)
|
||
|
if f == nil {
|
||
|
t.Fatalf("failed to get the filter with seed %d.", seed)
|
||
|
}
|
||
|
f.AllowP2P = true
|
||
|
total = 0
|
||
|
filters.NotifyWatchers(envelopes[0], true)
|
||
|
|
||
|
for i = 0; i < NumFilters; i++ {
|
||
|
mail = tst[i].f.Retrieve()
|
||
|
total += len(mail)
|
||
|
}
|
||
|
|
||
|
if total != 1 {
|
||
|
t.Fatalf("failed with seed %d: total: got %d, want 1.", seed, total)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func TestVariableTopics(t *testing.T) {
|
||
|
InitSingleTest()
|
||
|
|
||
|
const lastTopicByte = 3
|
||
|
var match bool
|
||
|
params, err := generateMessageParams()
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateMessageParams with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
msg, err := NewSentMessage(params)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed to create new message with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
env, err := msg.Wrap(params, time.Now())
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
f, err := generateFilter(t, true)
|
||
|
if err != nil {
|
||
|
t.Fatalf("failed generateFilter with seed %d: %s.", seed, err)
|
||
|
}
|
||
|
|
||
|
for i := 0; i < 4; i++ {
|
||
|
env.Topic = BytesToTopic(f.Topics[i])
|
||
|
match = f.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
t.Fatalf("failed MatchEnvelope symmetric with seed %d, step %d.", seed, i)
|
||
|
}
|
||
|
|
||
|
f.Topics[i][lastTopicByte]++
|
||
|
match = f.MatchEnvelope(env)
|
||
|
if !match {
|
||
|
// topic mismatch should have no affect, as topics are handled by topic matchers
|
||
|
t.Fatalf("MatchEnvelope symmetric with seed %d, step %d.", seed, i)
|
||
|
}
|
||
|
}
|
||
|
}
|