Skip to content

Commit b878e48

Browse files
committed
btcutil/bloom: align hash function count with filter capacity
In this commit, we ensure hash function parameters are consistent with filter size to avoid unnecessary operations. A filter with no capacity doesn't actually require any hash functions, so we can cut that loop short.
1 parent fa8d919 commit b878e48

2 files changed

Lines changed: 98 additions & 17 deletions

File tree

btcutil/bloom/filter.go

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,12 +76,21 @@ func NewFilter(elements, tweak uint32, fprate float64, flags wire.BloomUpdateTyp
7676
}
7777
}
7878

79+
// normalize adjusts filter parameters for consistency.
80+
func (bf *Filter) normalize() {
81+
if bf.msgFilterLoad != nil && len(bf.msgFilterLoad.Filter) == 0 {
82+
bf.msgFilterLoad.HashFuncs = 0
83+
}
84+
}
85+
7986
// LoadFilter creates a new Filter instance with the given underlying
8087
// wire.MsgFilterLoad.
8188
func LoadFilter(filter *wire.MsgFilterLoad) *Filter {
82-
return &Filter{
89+
bf := &Filter{
8390
msgFilterLoad: filter,
8491
}
92+
bf.normalize()
93+
return bf
8594
}
8695

8796
// IsLoaded returns true if a filter is loaded, otherwise false.
@@ -100,6 +109,7 @@ func (bf *Filter) IsLoaded() bool {
100109
func (bf *Filter) Reload(filter *wire.MsgFilterLoad) {
101110
bf.mtx.Lock()
102111
bf.msgFilterLoad = filter
112+
bf.normalize()
103113
bf.mtx.Unlock()
104114
}
105115

@@ -147,7 +157,8 @@ func (bf *Filter) matches(data []byte) bool {
147157
return false
148158
}
149159
}
150-
return true
160+
161+
return bf.msgFilterLoad.HashFuncs > 0
151162
}
152163

153164
// Matches returns true if the bloom filter might contain the passed data and

btcutil/bloom/filter_test.go

Lines changed: 85 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,19 +26,52 @@ func TestFilterLarge(t *testing.T) {
2626

2727
// TestFilterLoad ensures loading and unloading of a filter pass.
2828
func TestFilterLoad(t *testing.T) {
29-
merkle := wire.MsgFilterLoad{}
30-
31-
f := bloom.LoadFilter(&merkle)
32-
if !f.IsLoaded() {
33-
t.Errorf("TestFilterLoad IsLoaded test failed: want %v got %v",
34-
true, !f.IsLoaded())
35-
return
29+
// Test various filter configurations
30+
tests := []struct {
31+
name string
32+
filter *wire.MsgFilterLoad
33+
wantMatch bool
34+
}{
35+
{
36+
"normal filter",
37+
&wire.MsgFilterLoad{
38+
Filter: []byte{0x00},
39+
HashFuncs: 1,
40+
},
41+
false,
42+
},
43+
{
44+
"empty filter with funcs",
45+
&wire.MsgFilterLoad{
46+
Filter: []byte{},
47+
HashFuncs: 1,
48+
},
49+
false,
50+
},
51+
{
52+
"minimal filter",
53+
&wire.MsgFilterLoad{},
54+
false,
55+
},
3656
}
37-
f.Unload()
38-
if f.IsLoaded() {
39-
t.Errorf("TestFilterLoad IsLoaded test failed: want %v got %v",
40-
f.IsLoaded(), false)
41-
return
57+
58+
for _, test := range tests {
59+
f := bloom.LoadFilter(test.filter)
60+
if !f.IsLoaded() {
61+
t.Errorf("%s: IsLoaded test failed: "+
62+
"want true got false", test.name)
63+
continue
64+
}
65+
66+
if f.Matches([]byte("test")) != test.wantMatch {
67+
t.Errorf("%s: unexpected match result", test.name)
68+
}
69+
70+
f.Unload()
71+
if f.IsLoaded() {
72+
t.Errorf("%s: IsLoaded after Unload failed: "+
73+
"want false got true", test.name)
74+
}
4275
}
4376
}
4477

@@ -652,9 +685,46 @@ func TestFilterReload(t *testing.T) {
652685
t.Errorf("TestFilterReload LoadFilter test failed")
653686
return
654687
}
655-
bFilter.Reload(nil)
656688

657-
if bFilter.MsgFilterLoad() != nil {
658-
t.Errorf("TestFilterReload Reload test failed")
689+
reloadTests := []struct {
690+
name string
691+
filter *wire.MsgFilterLoad
692+
}{
693+
{
694+
name: "nil filter",
695+
filter: nil,
696+
},
697+
{
698+
name: "empty filter",
699+
filter: &wire.MsgFilterLoad{
700+
Filter: []byte{},
701+
HashFuncs: 3,
702+
},
703+
},
704+
{
705+
name: "normal filter",
706+
filter: &wire.MsgFilterLoad{
707+
Filter: []byte{0x00},
708+
HashFuncs: 1,
709+
},
710+
},
711+
}
712+
713+
for _, test := range reloadTests {
714+
bFilter.Reload(test.filter)
715+
if test.filter == nil {
716+
if bFilter.MsgFilterLoad() != nil {
717+
t.Errorf("%s: Reload test failed - "+
718+
"expected nil", test.name)
719+
}
720+
} else {
721+
bFilter.Add([]byte("test data"))
722+
if bFilter.Matches([]byte("test data")) &&
723+
len(test.filter.Filter) == 0 {
724+
725+
t.Errorf("%s: empty filter should "+
726+
"not match", test.name)
727+
}
728+
}
659729
}
660730
}

0 commit comments

Comments
 (0)