github.com/myafeier/go-ethereum@v1.6.8-0.20170719123245-3e0dbe0eaa72/eth/filters/filter_system_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 filters 18 19 import ( 20 "context" 21 "math/big" 22 "reflect" 23 "testing" 24 "time" 25 26 "github.com/ethereum/go-ethereum/common" 27 "github.com/ethereum/go-ethereum/core" 28 "github.com/ethereum/go-ethereum/core/types" 29 "github.com/ethereum/go-ethereum/ethdb" 30 "github.com/ethereum/go-ethereum/event" 31 "github.com/ethereum/go-ethereum/params" 32 "github.com/ethereum/go-ethereum/rpc" 33 ) 34 35 type testBackend struct { 36 mux *event.TypeMux 37 db ethdb.Database 38 } 39 40 func (b *testBackend) ChainDb() ethdb.Database { 41 return b.db 42 } 43 44 func (b *testBackend) EventMux() *event.TypeMux { 45 return b.mux 46 } 47 48 func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) { 49 var hash common.Hash 50 var num uint64 51 if blockNr == rpc.LatestBlockNumber { 52 hash = core.GetHeadBlockHash(b.db) 53 num = core.GetBlockNumber(b.db, hash) 54 } else { 55 num = uint64(blockNr) 56 hash = core.GetCanonicalHash(b.db, num) 57 } 58 return core.GetHeader(b.db, hash, num), nil 59 } 60 61 func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) { 62 num := core.GetBlockNumber(b.db, blockHash) 63 return core.GetBlockReceipts(b.db, blockHash, num), nil 64 } 65 66 // TestBlockSubscription tests if a block subscription returns block hashes for posted chain events. 67 // It creates multiple subscriptions: 68 // - one at the start and should receive all posted chain events and a second (blockHashes) 69 // - one that is created after a cutoff moment and uninstalled after a second cutoff moment (blockHashes[cutoff1:cutoff2]) 70 // - one that is created after the second cutoff moment (blockHashes[cutoff2:]) 71 func TestBlockSubscription(t *testing.T) { 72 t.Parallel() 73 74 var ( 75 mux = new(event.TypeMux) 76 db, _ = ethdb.NewMemDatabase() 77 backend = &testBackend{mux, db} 78 api = NewPublicFilterAPI(backend, false) 79 genesis = new(core.Genesis).MustCommit(db) 80 chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {}) 81 chainEvents = []core.ChainEvent{} 82 ) 83 84 for _, blk := range chain { 85 chainEvents = append(chainEvents, core.ChainEvent{Hash: blk.Hash(), Block: blk}) 86 } 87 88 chan0 := make(chan *types.Header) 89 sub0 := api.events.SubscribeNewHeads(chan0) 90 chan1 := make(chan *types.Header) 91 sub1 := api.events.SubscribeNewHeads(chan1) 92 93 go func() { // simulate client 94 i1, i2 := 0, 0 95 for i1 != len(chainEvents) || i2 != len(chainEvents) { 96 select { 97 case header := <-chan0: 98 if chainEvents[i1].Hash != header.Hash() { 99 t.Errorf("sub0 received invalid hash on index %d, want %x, got %x", i1, chainEvents[i1].Hash, header.Hash()) 100 } 101 i1++ 102 case header := <-chan1: 103 if chainEvents[i2].Hash != header.Hash() { 104 t.Errorf("sub1 received invalid hash on index %d, want %x, got %x", i2, chainEvents[i2].Hash, header.Hash()) 105 } 106 i2++ 107 } 108 } 109 110 sub0.Unsubscribe() 111 sub1.Unsubscribe() 112 }() 113 114 time.Sleep(1 * time.Second) 115 for _, e := range chainEvents { 116 mux.Post(e) 117 } 118 119 <-sub0.Err() 120 <-sub1.Err() 121 } 122 123 // TestPendingTxFilter tests whether pending tx filters retrieve all pending transactions that are posted to the event mux. 124 func TestPendingTxFilter(t *testing.T) { 125 t.Parallel() 126 127 var ( 128 mux = new(event.TypeMux) 129 db, _ = ethdb.NewMemDatabase() 130 backend = &testBackend{mux, db} 131 api = NewPublicFilterAPI(backend, false) 132 133 transactions = []*types.Transaction{ 134 types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), 135 types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), 136 types.NewTransaction(2, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), 137 types.NewTransaction(3, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), 138 types.NewTransaction(4, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil), 139 } 140 141 hashes []common.Hash 142 ) 143 144 fid0 := api.NewPendingTransactionFilter() 145 146 time.Sleep(1 * time.Second) 147 for _, tx := range transactions { 148 ev := core.TxPreEvent{Tx: tx} 149 mux.Post(ev) 150 } 151 152 for { 153 results, err := api.GetFilterChanges(fid0) 154 if err != nil { 155 t.Fatalf("Unable to retrieve logs: %v", err) 156 } 157 158 h := results.([]common.Hash) 159 hashes = append(hashes, h...) 160 if len(hashes) >= len(transactions) { 161 break 162 } 163 164 time.Sleep(100 * time.Millisecond) 165 } 166 167 for i := range hashes { 168 if hashes[i] != transactions[i].Hash() { 169 t.Errorf("hashes[%d] invalid, want %x, got %x", i, transactions[i].Hash(), hashes[i]) 170 } 171 } 172 } 173 174 // TestLogFilterCreation test whether a given filter criteria makes sense. 175 // If not it must return an error. 176 func TestLogFilterCreation(t *testing.T) { 177 var ( 178 mux = new(event.TypeMux) 179 db, _ = ethdb.NewMemDatabase() 180 backend = &testBackend{mux, db} 181 api = NewPublicFilterAPI(backend, false) 182 183 testCases = []struct { 184 crit FilterCriteria 185 success bool 186 }{ 187 // defaults 188 {FilterCriteria{}, true}, 189 // valid block number range 190 {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true}, 191 // "mined" block range to pending 192 {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true}, 193 // new mined and pending blocks 194 {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true}, 195 // from block "higher" than to block 196 {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false}, 197 // from block "higher" than to block 198 {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, 199 // from block "higher" than to block 200 {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false}, 201 // from block "higher" than to block 202 {FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false}, 203 } 204 ) 205 206 for i, test := range testCases { 207 _, err := api.NewFilter(test.crit) 208 if test.success && err != nil { 209 t.Errorf("expected filter creation for case %d to success, got %v", i, err) 210 } 211 if !test.success && err == nil { 212 t.Errorf("expected testcase %d to fail with an error", i) 213 } 214 } 215 } 216 217 // TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error 218 // when the filter is created. 219 func TestInvalidLogFilterCreation(t *testing.T) { 220 t.Parallel() 221 222 var ( 223 mux = new(event.TypeMux) 224 db, _ = ethdb.NewMemDatabase() 225 backend = &testBackend{mux, db} 226 api = NewPublicFilterAPI(backend, false) 227 ) 228 229 // different situations where log filter creation should fail. 230 // Reason: fromBlock > toBlock 231 testCases := []FilterCriteria{ 232 0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, 233 1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 234 2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, 235 } 236 237 for i, test := range testCases { 238 if _, err := api.NewFilter(test); err == nil { 239 t.Errorf("Expected NewFilter for case #%d to fail", i) 240 } 241 } 242 } 243 244 // TestLogFilter tests whether log filters match the correct logs that are posted to the event mux. 245 func TestLogFilter(t *testing.T) { 246 t.Parallel() 247 248 var ( 249 mux = new(event.TypeMux) 250 db, _ = ethdb.NewMemDatabase() 251 backend = &testBackend{mux, db} 252 api = NewPublicFilterAPI(backend, false) 253 254 firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") 255 secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") 256 thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") 257 notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") 258 firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 259 secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") 260 notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") 261 262 // posted twice, once as vm.Logs and once as core.PendingLogsEvent 263 allLogs = []*types.Log{ 264 {Address: firstAddr}, 265 {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, 266 {Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}, 267 {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 2}, 268 {Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}, 269 } 270 271 expectedCase7 = []*types.Log{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]} 272 expectedCase11 = []*types.Log{allLogs[1], allLogs[2], allLogs[1], allLogs[2]} 273 274 testCases = []struct { 275 crit FilterCriteria 276 expected []*types.Log 277 id rpc.ID 278 }{ 279 // match all 280 0: {FilterCriteria{}, allLogs, ""}, 281 // match none due to no matching addresses 282 1: {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, []*types.Log{}, ""}, 283 // match logs based on addresses, ignore topics 284 2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""}, 285 // match none due to no matching topics (match with address) 286 3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, ""}, 287 // match logs based on addresses and topics 288 4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[3:5], ""}, 289 // match logs based on multiple addresses and "or" topics 290 5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, allLogs[2:5], ""}, 291 // logs in the pending block 292 6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""}, 293 // mined logs with block num >= 2 or pending logs 294 7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""}, 295 // all "mined" logs with block num >= 2 296 8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""}, 297 // all "mined" logs 298 9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""}, 299 // all "mined" logs with 1>= block num <=2 and topic secondTopic 300 10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{{secondTopic}}}, allLogs[3:4], ""}, 301 // all "mined" and pending logs with topic firstTopic 302 11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{{firstTopic}}}, expectedCase11, ""}, 303 } 304 ) 305 306 // create all filters 307 for i := range testCases { 308 testCases[i].id, _ = api.NewFilter(testCases[i].crit) 309 } 310 311 // raise events 312 time.Sleep(1 * time.Second) 313 if err := mux.Post(allLogs); err != nil { 314 t.Fatal(err) 315 } 316 if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil { 317 t.Fatal(err) 318 } 319 320 for i, tt := range testCases { 321 var fetched []*types.Log 322 for { // fetch all expected logs 323 results, err := api.GetFilterChanges(tt.id) 324 if err != nil { 325 t.Fatalf("Unable to fetch logs: %v", err) 326 } 327 328 fetched = append(fetched, results.([]*types.Log)...) 329 if len(fetched) >= len(tt.expected) { 330 break 331 } 332 333 time.Sleep(100 * time.Millisecond) 334 } 335 336 if len(fetched) != len(tt.expected) { 337 t.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) 338 return 339 } 340 341 for l := range fetched { 342 if fetched[l].Removed { 343 t.Errorf("expected log not to be removed for log %d in case %d", l, i) 344 } 345 if !reflect.DeepEqual(fetched[l], tt.expected[l]) { 346 t.Errorf("invalid log on index %d for case %d", l, i) 347 } 348 } 349 } 350 } 351 352 // TestPendingLogsSubscription tests if a subscription receives the correct pending logs that are posted to the event mux. 353 func TestPendingLogsSubscription(t *testing.T) { 354 t.Parallel() 355 356 var ( 357 mux = new(event.TypeMux) 358 db, _ = ethdb.NewMemDatabase() 359 backend = &testBackend{mux, db} 360 api = NewPublicFilterAPI(backend, false) 361 362 firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111") 363 secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222") 364 thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333") 365 notUsedAddress = common.HexToAddress("0x9999999999999999999999999999999999999999") 366 firstTopic = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111") 367 secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222") 368 thirdTopic = common.HexToHash("0x3333333333333333333333333333333333333333333333333333333333333333") 369 forthTopic = common.HexToHash("0x4444444444444444444444444444444444444444444444444444444444444444") 370 notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999") 371 372 allLogs = []core.PendingLogsEvent{ 373 {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{}, BlockNumber: 0}}}, 374 {Logs: []*types.Log{{Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 1}}}, 375 {Logs: []*types.Log{{Address: secondAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 2}}}, 376 {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 3}}}, 377 {Logs: []*types.Log{{Address: thirdAddress, Topics: []common.Hash{secondTopic}, BlockNumber: 4}}}, 378 {Logs: []*types.Log{ 379 {Address: thirdAddress, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, 380 {Address: thirdAddress, Topics: []common.Hash{thirdTopic}, BlockNumber: 5}, 381 {Address: thirdAddress, Topics: []common.Hash{forthTopic}, BlockNumber: 5}, 382 {Address: firstAddr, Topics: []common.Hash{firstTopic}, BlockNumber: 5}, 383 }}, 384 } 385 386 convertLogs = func(pl []core.PendingLogsEvent) []*types.Log { 387 var logs []*types.Log 388 for _, l := range pl { 389 logs = append(logs, l.Logs...) 390 } 391 return logs 392 } 393 394 testCases = []struct { 395 crit FilterCriteria 396 expected []*types.Log 397 c chan []*types.Log 398 sub *Subscription 399 }{ 400 // match all 401 {FilterCriteria{}, convertLogs(allLogs), nil, nil}, 402 // match none due to no matching addresses 403 {FilterCriteria{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{{}}}, []*types.Log{}, nil, nil}, 404 // match logs based on addresses, ignore topics 405 {FilterCriteria{Addresses: []common.Address{firstAddr}}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil}, 406 // match none due to no matching topics (match with address) 407 {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}}, []*types.Log{}, nil, nil}, 408 // match logs based on addresses and topics 409 {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[3:5]), allLogs[5].Logs[0]), nil, nil}, 410 // match logs based on multiple addresses and "or" topics 411 {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}}, append(convertLogs(allLogs[2:5]), allLogs[5].Logs[0]), nil, nil}, 412 // block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes 413 {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)}, append(convertLogs(allLogs[:2]), allLogs[5].Logs[3]), nil, nil}, 414 // multiple pending logs, should match only 2 topics from the logs in block 5 415 {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, forthTopic}}}, []*types.Log{allLogs[5].Logs[0], allLogs[5].Logs[2]}, nil, nil}, 416 } 417 ) 418 419 // create all subscriptions, this ensures all subscriptions are created before the events are posted. 420 // on slow machines this could otherwise lead to missing events when the subscription is created after 421 // (some) events are posted. 422 for i := range testCases { 423 testCases[i].c = make(chan []*types.Log) 424 testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c) 425 } 426 427 for n, test := range testCases { 428 i := n 429 tt := test 430 go func() { 431 var fetched []*types.Log 432 fetchLoop: 433 for { 434 logs := <-tt.c 435 fetched = append(fetched, logs...) 436 if len(fetched) >= len(tt.expected) { 437 break fetchLoop 438 } 439 } 440 441 if len(fetched) != len(tt.expected) { 442 t.Fatalf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)) 443 } 444 445 for l := range fetched { 446 if fetched[l].Removed { 447 t.Errorf("expected log not to be removed for log %d in case %d", l, i) 448 } 449 if !reflect.DeepEqual(fetched[l], tt.expected[l]) { 450 t.Errorf("invalid log on index %d for case %d", l, i) 451 } 452 } 453 }() 454 } 455 456 // raise events 457 time.Sleep(1 * time.Second) 458 for _, l := range allLogs { 459 if err := mux.Post(l); err != nil { 460 t.Fatal(err) 461 } 462 } 463 }