aboutsummaryrefslogblamecommitdiffstats
path: root/whisper/whisperv5/filter_test.go
blob: 1cf85b8d7ed461a7dfe1dff258c293f5eda5c2e2 (plain) (tree)



















                                                                                  
                         













                                                            
                        



                             
                        



                            
                     



                   
                                                                    





                                                           
                                          




                                        
                                                                       





                                           
                                    



                                                             
                                                                               







                                       
                                                                            

                                                        
                                               
                              
                                                          



                    
                                       


                                   
                  
                                
                                                    
 

                     
                                              



                                                                                    
                             


                                                                                    

         


                                                      


                 

                                               
                                     

                                                                                                     
                 

                                                                                                   



                 
                                      



                                         
                                                                                     


                                         
                                                                                      
         
                                                            
                                                                 


                                
                        

                                         
                                                                                     
         
                                                            
                                                        


         
                                      

                        
                                            
                       
                                                                              

         
                                              
                       
                                                                                



                                              
                                                                                     







                                                 
                                                                    


                                        
                                                                              


                                        
                                                                               


                                
                            




                                      
                                                                      




                                            
                                                                                




                                                
                                                                                 





                                                                  
                                                                                                                              





                                                             
                                                                                                                 

         
                                                       

                                 


                                                                                                





                                        
                                                                           





                                    
                                                                      




                                       
                                                                                                




                                                   
                                                                                                    





                                             
                                                                                                 

         
                                                              


                                        
                                                                                                       





                                                  
                                                                                                   





                                             














                                                                                                                   


         
                                        



                                              
                                                                                     

         
                                         
                       
                                                                              








                                             
                                                                    



                          
                                                           



                                
                                                                                 





                                          
                                                                               




                                     
                                                                                      




                                 
                                                                                    




                                
                                                                                    





                                
                                                                                  





                                 
                                                                                

         
                            


                                    
                                                                                       


                                 
                                                                                    





                                             
                                                                           

                                
                                                                                                


         
                                         

                        
                                          
                       
                                                                              



                                              
                                                                                     










                                             
                                                                    



                          
                                                              



                                
                                                                                 





                                          
                                                                              




                                     
                                                                                      




                                 
                                                                                    

         
                         
                            

                                                                                    







                                      
                                                                                  





                                     
                                                                                





                                     
                                                                                                















                                                               
                                                                   

                                              
                                                                                     







                                             
                                                                    




                          
                                 



                               

                    
                       

                             
 
                  
                                
                                               

                                        







                                                                                         

         
                   


                                            
                                               
                                                          




                                         
                                                           












                                          
                                                                                                




                                          
                                                                         


                                              
                                                                                                                      





                                             
                                 
                 
                              







                                                                         
                                                  


                                         
                                               
                                                          




                                         
                                                           









                                                    
                                                                                                  


                                 
                                                                                                        


                                    
                                                                                                              




                                          
                                                                         


                                              
                                                                                                                   





                         
                                                  






                                          
                                                                                    

         
                                 


                                                                        

                          
                                                  






                                          
                                                                                    

         
// Copyright 2016 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum 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 go-ethereum library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// 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 go-ethereum library. If not, see <http://www.gnu.org/licenses/>.

package whisperv5

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 = make(map[common.Hash]*ReceivedMessage)

    const topicNum = 8
    f.Topics = make([]TopicType, topicNum)
    for i := 0; i < topicNum; i++ {
        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, 12)
        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()
    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) != 40 {
            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 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 // ensure mismatch

    // mismatch with pseudo-random data
    msg := NewSentMessage(params)
    env, err := msg.Wrap(params)
    if err != nil {
        t.Fatalf("failed Wrap with seed %d: %s.", seed, err)
    }
    match := fsym.MatchEnvelope(env)
    if match {
        t.Fatalf("failed MatchEnvelope symmetric with seed %d.", seed)
    }
    match = fasym.MatchEnvelope(env)
    if match {
        t.Fatalf("failed MatchEnvelope asymmetric with seed %d.", seed)
    }

    // encrypt symmetrically
    i := mrand.Int() % 4
    fsym.Topics[i] = params.Topic
    fasym.Topics[i] = params.Topic
    msg = NewSentMessage(params)
    env, err = msg.Wrap(params)
    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)
    }

    // asymmetric + matching topic: mismatch
    match = fasym.MatchEnvelope(env)
    if match {
        t.Fatalf("failed MatchEnvelope() asymmetric 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 = NewSentMessage(params)
    env, err = msg.Wrap(params)
    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 {
        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 = f.Topics[index]

    sentMessage := NewSentMessage(params)
    env, err := sentMessage.Wrap(params)
    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 mismatch
    if f.MatchMessage(msg) {
        t.Fatalf("failed MatchMessage(src mismatch) 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) {
        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 = f.Topics[index]
    params.Dst = &f.KeyAsym.PublicKey
    keySymOrig := params.KeySym
    params.KeySym = nil

    sentMessage := NewSentMessage(params)
    env, err := sentMessage.Wrap(params)
    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 mismatch
    if f.MatchMessage(msg) {
        t.Fatalf("failed MatchMessage(src mismatch) 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) {
        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 = make(map[common.Hash]*ReceivedMessage)
    clone.Src = orig.Src
    clone.KeyAsym = orig.KeyAsym
    clone.KeySym = orig.KeySym
    clone.Topics = orig.Topics
    clone.PoW = orig.PoW
    clone.AcceptP2P = orig.AcceptP2P
    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 = f.Topics[2]
    sentMessage := NewSentMessage(params)
    env, err := sentMessage.Wrap(params)
    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()
    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.AcceptP2P = 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)
    }
}