github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/core/bloombits/matcher_test.go (about) 1 2 //<developer> 3 // <name>linapex 曹一峰</name> 4 // <email>linapex@163.com</email> 5 // <wx>superexc</wx> 6 // <qqgroup>128148617</qqgroup> 7 // <url>https://jsq.ink</url> 8 // <role>pku engineer</role> 9 // <date>2019-03-16 19:16:35</date> 10 //</624450078257451008> 11 12 13 package bloombits 14 15 import ( 16 "context" 17 "math/rand" 18 "sync/atomic" 19 "testing" 20 "time" 21 22 "github.com/ethereum/go-ethereum/common" 23 ) 24 25 const testSectionSize = 4096 26 27 //测试通配符筛选规则(nil)是否可以指定并且处理得很好。 28 func TestMatcherWildcards(t *testing.T) { 29 matcher := NewMatcher(testSectionSize, [][][]byte{ 30 {common.Address{}.Bytes(), common.Address{0x01}.Bytes()}, //默认地址不是通配符 31 {common.Hash{}.Bytes(), common.Hash{0x01}.Bytes()}, //默认哈希不是通配符 32 {common.Hash{0x01}.Bytes()}, //简单规则,健全检查 33 {common.Hash{0x01}.Bytes(), nil}, //通配符后缀,删除规则 34 {nil, common.Hash{0x01}.Bytes()}, //通配符前缀,删除规则 35 {nil, nil}, //通配符组合,删除规则 36 {}, //初始化通配符规则,删除规则 37 nil, //适当的通配符规则,删除规则 38 }) 39 if len(matcher.filters) != 3 { 40 t.Fatalf("filter system size mismatch: have %d, want %d", len(matcher.filters), 3) 41 } 42 if len(matcher.filters[0]) != 2 { 43 t.Fatalf("address clause size mismatch: have %d, want %d", len(matcher.filters[0]), 2) 44 } 45 if len(matcher.filters[1]) != 2 { 46 t.Fatalf("combo topic clause size mismatch: have %d, want %d", len(matcher.filters[1]), 2) 47 } 48 if len(matcher.filters[2]) != 1 { 49 t.Fatalf("singletone topic clause size mismatch: have %d, want %d", len(matcher.filters[2]), 1) 50 } 51 } 52 53 //在单个连续工作流上测试匹配器管道,而不中断。 54 func TestMatcherContinuous(t *testing.T) { 55 testMatcherDiffBatches(t, [][]bloomIndexes{{{10, 20, 30}}}, 0, 100000, false, 75) 56 testMatcherDiffBatches(t, [][]bloomIndexes{{{32, 3125, 100}}, {{40, 50, 10}}}, 0, 100000, false, 81) 57 testMatcherDiffBatches(t, [][]bloomIndexes{{{4, 8, 11}, {7, 8, 17}}, {{9, 9, 12}, {15, 20, 13}}, {{18, 15, 15}, {12, 10, 4}}}, 0, 10000, false, 36) 58 } 59 60 //在不断中断和恢复的工作模式下测试匹配器管道 61 //为了确保数据项只被请求一次。 62 func TestMatcherIntermittent(t *testing.T) { 63 testMatcherDiffBatches(t, [][]bloomIndexes{{{10, 20, 30}}}, 0, 100000, true, 75) 64 testMatcherDiffBatches(t, [][]bloomIndexes{{{32, 3125, 100}}, {{40, 50, 10}}}, 0, 100000, true, 81) 65 testMatcherDiffBatches(t, [][]bloomIndexes{{{4, 8, 11}, {7, 8, 17}}, {{9, 9, 12}, {15, 20, 13}}, {{18, 15, 15}, {12, 10, 4}}}, 0, 10000, true, 36) 66 } 67 68 //在随机输入上测试匹配器管道,以期捕获异常。 69 func TestMatcherRandom(t *testing.T) { 70 for i := 0; i < 10; i++ { 71 testMatcherBothModes(t, makeRandomIndexes([]int{1}, 50), 0, 10000, 0) 72 testMatcherBothModes(t, makeRandomIndexes([]int{3}, 50), 0, 10000, 0) 73 testMatcherBothModes(t, makeRandomIndexes([]int{2, 2, 2}, 20), 0, 10000, 0) 74 testMatcherBothModes(t, makeRandomIndexes([]int{5, 5, 5}, 50), 0, 10000, 0) 75 testMatcherBothModes(t, makeRandomIndexes([]int{4, 4, 4}, 20), 0, 10000, 0) 76 } 77 } 78 79 //如果起始块是 80 //从8的倍数换档。这需要包括优化 81 //位集匹配https://github.com/ethereum/go-ethereum/issues/15309。 82 func TestMatcherShifted(t *testing.T) { 83 //块0在测试中始终匹配,跳过前8个块 84 //开始在匹配器位集中获取一个可能的零字节。 85 86 //要保持第二个位集字节为零,筛选器必须只与第一个匹配 87 //块16中的时间,所以做一个全16位的过滤器就足够了。 88 89 //为了使起始块不被8整除,块号9是第一个 90 //这将引入移位,而不匹配块0。 91 testMatcherBothModes(t, [][]bloomIndexes{{{16, 16, 16}}}, 9, 64, 0) 92 } 93 94 //所有匹配的测试不会崩溃(内部特殊情况)。 95 func TestWildcardMatcher(t *testing.T) { 96 testMatcherBothModes(t, nil, 0, 10000, 0) 97 } 98 99 //makerandomndexes生成一个由多个过滤器组成的随机过滤器系统。 100 //标准,每个都有一个地址和任意的Bloom列表组件 101 //许多主题Bloom列出组件。 102 func makeRandomIndexes(lengths []int, max int) [][]bloomIndexes { 103 res := make([][]bloomIndexes, len(lengths)) 104 for i, topics := range lengths { 105 res[i] = make([]bloomIndexes, topics) 106 for j := 0; j < topics; j++ { 107 for k := 0; k < len(res[i][j]); k++ { 108 res[i][j][k] = uint(rand.Intn(max-1) + 2) 109 } 110 } 111 } 112 return res 113 } 114 115 //testmacherdiffbatches在单个传递中运行给定的匹配测试,并且 116 //在批量交付模式下,验证是否处理了所有类型的交付。 117 //正确无误。 118 func testMatcherDiffBatches(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, intermittent bool, retrievals uint32) { 119 singleton := testMatcher(t, filter, start, blocks, intermittent, retrievals, 1) 120 batched := testMatcher(t, filter, start, blocks, intermittent, retrievals, 16) 121 122 if singleton != batched { 123 t.Errorf("filter = %v blocks = %v intermittent = %v: request count mismatch, %v in signleton vs. %v in batched mode", filter, blocks, intermittent, singleton, batched) 124 } 125 } 126 127 //TestMatcherBothModes在连续和 128 //在间歇模式下,验证请求计数是否匹配。 129 func testMatcherBothModes(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, retrievals uint32) { 130 continuous := testMatcher(t, filter, start, blocks, false, retrievals, 16) 131 intermittent := testMatcher(t, filter, start, blocks, true, retrievals, 16) 132 133 if continuous != intermittent { 134 t.Errorf("filter = %v blocks = %v: request count mismatch, %v in continuous vs. %v in intermittent mode", filter, blocks, continuous, intermittent) 135 } 136 } 137 138 //TestMatcher是一个通用测试程序,用于运行给定的Matcher测试并返回 139 //在不同模式之间进行交叉验证的请求数。 140 func testMatcher(t *testing.T, filter [][]bloomIndexes, start, blocks uint64, intermittent bool, retrievals uint32, maxReqCount int) uint32 { 141 //创建一个新的匹配器,模拟显式随机位集 142 matcher := NewMatcher(testSectionSize, nil) 143 matcher.filters = filter 144 145 for _, rule := range filter { 146 for _, topic := range rule { 147 for _, bit := range topic { 148 matcher.addScheduler(bit) 149 } 150 } 151 } 152 //跟踪发出的检索请求数 153 var requested uint32 154 155 //启动筛选器和检索器goroutine的匹配会话 156 quit := make(chan struct{}) 157 matches := make(chan uint64, 16) 158 159 session, err := matcher.Start(context.Background(), start, blocks-1, matches) 160 if err != nil { 161 t.Fatalf("failed to stat matcher session: %v", err) 162 } 163 startRetrievers(session, quit, &requested, maxReqCount) 164 165 //遍历所有块并验证管道是否生成正确的匹配项 166 for i := start; i < blocks; i++ { 167 if expMatch3(filter, i) { 168 match, ok := <-matches 169 if !ok { 170 t.Errorf("filter = %v blocks = %v intermittent = %v: expected #%v, results channel closed", filter, blocks, intermittent, i) 171 return 0 172 } 173 if match != i { 174 t.Errorf("filter = %v blocks = %v intermittent = %v: expected #%v, got #%v", filter, blocks, intermittent, i, match) 175 } 176 //如果我们正在测试间歇模式,请中止并重新启动管道。 177 if intermittent { 178 session.Close() 179 close(quit) 180 181 quit = make(chan struct{}) 182 matches = make(chan uint64, 16) 183 184 session, err = matcher.Start(context.Background(), i+1, blocks-1, matches) 185 if err != nil { 186 t.Fatalf("failed to stat matcher session: %v", err) 187 } 188 startRetrievers(session, quit, &requested, maxReqCount) 189 } 190 } 191 } 192 //确保结果通道在最后一个块后被拆除 193 match, ok := <-matches 194 if ok { 195 t.Errorf("filter = %v blocks = %v intermittent = %v: expected closed channel, got #%v", filter, blocks, intermittent, match) 196 } 197 //清理会话并确保与预期的检索计数匹配 198 session.Close() 199 close(quit) 200 201 if retrievals != 0 && requested != retrievals { 202 t.Errorf("filter = %v blocks = %v intermittent = %v: request count mismatch, have #%v, want #%v", filter, blocks, intermittent, requested, retrievals) 203 } 204 return requested 205 } 206 207 //StartRetriever启动一批Goroutines,用于侦听节请求 208 //为他们服务。 209 func startRetrievers(session *MatcherSession, quit chan struct{}, retrievals *uint32, batch int) { 210 requests := make(chan chan *Retrieval) 211 212 for i := 0; i < 10; i++ { 213 //启动多路复用器以测试多线程执行 214 go session.Multiplex(batch, 100*time.Microsecond, requests) 215 216 //启动与上述多路复用器匹配的服务 217 go func() { 218 for { 219 //等待服务请求或关闭 220 select { 221 case <-quit: 222 return 223 224 case request := <-requests: 225 task := <-request 226 227 task.Bitsets = make([][]byte, len(task.Sections)) 228 for i, section := range task.Sections { 229 if rand.Int()%4 != 0 { //处理偶尔丢失的交货 230 task.Bitsets[i] = generateBitset(task.Bit, section) 231 atomic.AddUint32(retrievals, 1) 232 } 233 } 234 request <- task 235 } 236 } 237 }() 238 } 239 } 240 241 //generateBitset为给定的bloom位和节生成旋转的位集 242 //数字。 243 func generateBitset(bit uint, section uint64) []byte { 244 bitset := make([]byte, testSectionSize/8) 245 for i := 0; i < len(bitset); i++ { 246 for b := 0; b < 8; b++ { 247 blockIdx := section*testSectionSize + uint64(i*8+b) 248 bitset[i] += bitset[i] 249 if (blockIdx % uint64(bit)) == 0 { 250 bitset[i]++ 251 } 252 } 253 } 254 return bitset 255 } 256 257 func expMatch1(filter bloomIndexes, i uint64) bool { 258 for _, ii := range filter { 259 if (i % uint64(ii)) != 0 { 260 return false 261 } 262 } 263 return true 264 } 265 266 func expMatch2(filter []bloomIndexes, i uint64) bool { 267 for _, ii := range filter { 268 if expMatch1(ii, i) { 269 return true 270 } 271 } 272 return false 273 } 274 275 func expMatch3(filter [][]bloomIndexes, i uint64) bool { 276 for _, ii := range filter { 277 if !expMatch2(ii, i) { 278 return false 279 } 280 } 281 return true 282 } 283