github.com/ethereumproject/go-ethereum@v5.5.2+incompatible/eth/filters/api.go (about) 1 // Copyright 2015 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 "crypto/rand" 22 "encoding/hex" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "sync" 27 "time" 28 29 "github.com/ethereumproject/go-ethereum/common" 30 "github.com/ethereumproject/go-ethereum/core/types" 31 "github.com/ethereumproject/go-ethereum/core/vm" 32 "github.com/ethereumproject/go-ethereum/ethdb" 33 "github.com/ethereumproject/go-ethereum/event" 34 "github.com/ethereumproject/go-ethereum/rpc" 35 ) 36 37 var ( 38 filterTickerTime = 5 * time.Minute 39 ) 40 41 // byte will be inferred 42 const ( 43 unknownFilterTy = iota 44 blockFilterTy 45 transactionFilterTy 46 logFilterTy 47 ) 48 49 // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various 50 // information related to the Ethereum protocol such als blocks, transactions and logs. 51 type PublicFilterAPI struct { 52 mux *event.TypeMux 53 54 quit chan struct{} 55 chainDb ethdb.Database 56 57 filterManager *FilterSystem 58 59 filterMapMu sync.RWMutex 60 filterMapping map[string]int // maps between filter internal filter identifiers and external filter identifiers 61 62 logMu sync.RWMutex 63 logQueue map[int]*logQueue 64 65 blockMu sync.RWMutex 66 blockQueue map[int]*hashQueue 67 68 transactionMu sync.RWMutex 69 transactionQueue map[int]*hashQueue 70 } 71 72 // NewPublicFilterAPI returns a new PublicFilterAPI instance. 73 func NewPublicFilterAPI(chainDb ethdb.Database, mux *event.TypeMux) *PublicFilterAPI { 74 svc := &PublicFilterAPI{ 75 mux: mux, 76 chainDb: chainDb, 77 filterManager: NewFilterSystem(mux), 78 filterMapping: make(map[string]int), 79 logQueue: make(map[int]*logQueue), 80 blockQueue: make(map[int]*hashQueue), 81 transactionQueue: make(map[int]*hashQueue), 82 } 83 go svc.start() 84 return svc 85 } 86 87 // Stop quits the work loop. 88 func (s *PublicFilterAPI) Stop() { 89 close(s.quit) 90 } 91 92 // start the work loop, wait and process events. 93 func (s *PublicFilterAPI) start() { 94 timer := time.NewTicker(2 * time.Second) 95 defer timer.Stop() 96 done: 97 for { 98 select { 99 case <-timer.C: 100 s.filterManager.Lock() // lock order like filterLoop() 101 s.logMu.Lock() 102 for id, filter := range s.logQueue { 103 if time.Since(filter.timeout) > filterTickerTime { 104 s.filterManager.Remove(id) 105 delete(s.logQueue, id) 106 } 107 } 108 s.logMu.Unlock() 109 110 s.blockMu.Lock() 111 for id, filter := range s.blockQueue { 112 if time.Since(filter.timeout) > filterTickerTime { 113 s.filterManager.Remove(id) 114 delete(s.blockQueue, id) 115 } 116 } 117 s.blockMu.Unlock() 118 119 s.transactionMu.Lock() 120 for id, filter := range s.transactionQueue { 121 if time.Since(filter.timeout) > filterTickerTime { 122 s.filterManager.Remove(id) 123 delete(s.transactionQueue, id) 124 } 125 } 126 s.transactionMu.Unlock() 127 s.filterManager.Unlock() 128 case <-s.quit: 129 break done 130 } 131 } 132 133 } 134 135 // NewBlockFilter create a new filter that returns blocks that are included into the canonical chain. 136 func (s *PublicFilterAPI) NewBlockFilter() (string, error) { 137 // protect filterManager.Add() and setting of filter fields 138 s.filterManager.Lock() 139 defer s.filterManager.Unlock() 140 141 externalId, err := newFilterId() 142 if err != nil { 143 return "", err 144 } 145 146 filter := New(s.chainDb) 147 id, err := s.filterManager.Add(filter, ChainFilter) 148 if err != nil { 149 return "", err 150 } 151 152 s.blockMu.Lock() 153 s.blockQueue[id] = &hashQueue{timeout: time.Now()} 154 s.blockMu.Unlock() 155 156 filter.BlockCallback = func(block *types.Block, logs vm.Logs) { 157 s.blockMu.Lock() 158 defer s.blockMu.Unlock() 159 160 if queue := s.blockQueue[id]; queue != nil { 161 queue.add(block.Hash()) 162 } 163 } 164 165 s.filterMapMu.Lock() 166 s.filterMapping[externalId] = id 167 s.filterMapMu.Unlock() 168 169 return externalId, nil 170 } 171 172 // NewPendingTransactionFilter creates a filter that returns new pending transactions. 173 func (s *PublicFilterAPI) NewPendingTransactionFilter() (string, error) { 174 // protect filterManager.Add() and setting of filter fields 175 s.filterManager.Lock() 176 defer s.filterManager.Unlock() 177 178 externalId, err := newFilterId() 179 if err != nil { 180 return "", err 181 } 182 183 filter := New(s.chainDb) 184 id, err := s.filterManager.Add(filter, PendingTxFilter) 185 if err != nil { 186 return "", err 187 } 188 189 s.transactionMu.Lock() 190 s.transactionQueue[id] = &hashQueue{timeout: time.Now()} 191 s.transactionMu.Unlock() 192 193 filter.TransactionCallback = func(tx *types.Transaction) { 194 s.transactionMu.Lock() 195 defer s.transactionMu.Unlock() 196 197 if queue := s.transactionQueue[id]; queue != nil { 198 queue.add(tx.Hash()) 199 } 200 } 201 202 s.filterMapMu.Lock() 203 s.filterMapping[externalId] = id 204 s.filterMapMu.Unlock() 205 206 return externalId, nil 207 } 208 209 // newLogFilter creates a new log filter. 210 func (s *PublicFilterAPI) newLogFilter(earliest, latest int64, addresses []common.Address, topics [][]common.Hash, callback func(log *vm.Log, removed bool)) (int, error) { 211 // protect filterManager.Add() and setting of filter fields 212 s.filterManager.Lock() 213 defer s.filterManager.Unlock() 214 215 filter := New(s.chainDb) 216 id, err := s.filterManager.Add(filter, LogFilter) 217 if err != nil { 218 return 0, err 219 } 220 221 s.logMu.Lock() 222 s.logQueue[id] = &logQueue{timeout: time.Now()} 223 s.logMu.Unlock() 224 225 filter.SetBeginBlock(earliest) 226 filter.SetEndBlock(latest) 227 filter.SetAddresses(addresses) 228 filter.SetTopics(topics) 229 filter.LogCallback = func(log *vm.Log, removed bool) { 230 if callback != nil { 231 callback(log, removed) 232 } else { 233 s.logMu.Lock() 234 defer s.logMu.Unlock() 235 if queue := s.logQueue[id]; queue != nil { 236 queue.add(vmlog{log, removed}) 237 } 238 } 239 } 240 241 return id, nil 242 } 243 244 // Logs creates a subscription that fires for all new log that match the given filter criteria. 245 func (s *PublicFilterAPI) Logs(ctx context.Context, args NewFilterArgs) (rpc.Subscription, error) { 246 notifier, supported := rpc.NotifierFromContext(ctx) 247 if !supported { 248 return nil, rpc.ErrNotificationsUnsupported 249 } 250 251 var ( 252 externalId string 253 subscription rpc.Subscription 254 err error 255 ) 256 257 if externalId, err = newFilterId(); err != nil { 258 return nil, err 259 } 260 261 // uninstall filter when subscription is unsubscribed/cancelled 262 if subscription, err = notifier.NewSubscription(func(string) { 263 s.UninstallFilter(externalId) 264 }); err != nil { 265 return nil, err 266 } 267 268 notifySubscriber := func(log *vm.Log, removed bool) { 269 rpcLog := toRPCLogs(vm.Logs{log}, removed) 270 if err := subscription.Notify(rpcLog); err != nil { 271 subscription.Cancel() 272 } 273 } 274 275 // from and to block number are not used since subscriptions don't allow you to travel to "time" 276 var id int 277 if len(args.Addresses) > 0 { 278 id, err = s.newLogFilter(-1, -1, args.Addresses, args.Topics, notifySubscriber) 279 } else { 280 id, err = s.newLogFilter(-1, -1, nil, args.Topics, notifySubscriber) 281 } 282 283 if err != nil { 284 subscription.Cancel() 285 return nil, err 286 } 287 288 s.filterMapMu.Lock() 289 s.filterMapping[externalId] = id 290 s.filterMapMu.Unlock() 291 292 return subscription, err 293 } 294 295 // NewFilterArgs represents a request to create a new filter. 296 type NewFilterArgs struct { 297 FromBlock rpc.BlockNumber 298 ToBlock rpc.BlockNumber 299 Addresses []common.Address 300 Topics [][]common.Hash 301 } 302 303 // UnmarshalJSON sets *args fields with given data. 304 func (args *NewFilterArgs) UnmarshalJSON(data []byte) error { 305 type input struct { 306 From *rpc.BlockNumber `json:"fromBlock"` 307 ToBlock *rpc.BlockNumber `json:"toBlock"` 308 Addresses interface{} `json:"address"` 309 Topics []interface{} `json:"topics"` 310 } 311 312 var raw input 313 if err := json.Unmarshal(data, &raw); err != nil { 314 return err 315 } 316 317 if raw.From == nil || raw.From.Int64() < 0 { 318 args.FromBlock = rpc.LatestBlockNumber 319 } else { 320 args.FromBlock = *raw.From 321 } 322 323 if raw.ToBlock == nil || raw.ToBlock.Int64() < 0 { 324 args.ToBlock = rpc.LatestBlockNumber 325 } else { 326 args.ToBlock = *raw.ToBlock 327 } 328 329 args.Addresses = []common.Address{} 330 331 if raw.Addresses != nil { 332 // raw.Address can contain a single address or an array of addresses 333 var addresses []common.Address 334 if strAddrs, ok := raw.Addresses.([]interface{}); ok { 335 for i, addr := range strAddrs { 336 if strAddr, ok := addr.(string); ok { 337 if len(strAddr) >= 2 && strAddr[0] == '0' && (strAddr[1] == 'x' || strAddr[1] == 'X') { 338 strAddr = strAddr[2:] 339 } 340 if decAddr, err := hex.DecodeString(strAddr); err == nil { 341 addresses = append(addresses, common.BytesToAddress(decAddr)) 342 } else { 343 return fmt.Errorf("invalid address given") 344 } 345 } else { 346 return fmt.Errorf("invalid address on index %d", i) 347 } 348 } 349 } else if singleAddr, ok := raw.Addresses.(string); ok { 350 if len(singleAddr) >= 2 && singleAddr[0] == '0' && (singleAddr[1] == 'x' || singleAddr[1] == 'X') { 351 singleAddr = singleAddr[2:] 352 } 353 if decAddr, err := hex.DecodeString(singleAddr); err == nil { 354 addresses = append(addresses, common.BytesToAddress(decAddr)) 355 } else { 356 return fmt.Errorf("invalid address given") 357 } 358 } else { 359 return errors.New("invalid address(es) given") 360 } 361 args.Addresses = addresses 362 } 363 364 // helper function which parses a string to a topic hash 365 topicConverter := func(raw string) (common.Hash, error) { 366 if len(raw) == 0 { 367 return common.Hash{}, nil 368 } 369 if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') { 370 raw = raw[2:] 371 } 372 if len(raw) != 2*common.HashLength { 373 return common.Hash{}, errors.New("invalid topic(s)") 374 } 375 if decAddr, err := hex.DecodeString(raw); err == nil { 376 return common.BytesToHash(decAddr), nil 377 } 378 return common.Hash{}, errors.New("invalid topic(s)") 379 } 380 381 // topics is an array consisting of strings and/or arrays of strings. 382 // JSON null values are converted to common.Hash{} and ignored by the filter manager. 383 if len(raw.Topics) > 0 { 384 args.Topics = make([][]common.Hash, len(raw.Topics)) 385 for i, t := range raw.Topics { 386 if t == nil { // ignore topic when matching logs 387 args.Topics[i] = []common.Hash{{}} 388 } else if topic, ok := t.(string); ok { // match specific topic 389 top, err := topicConverter(topic) 390 if err != nil { 391 return err 392 } 393 args.Topics[i] = []common.Hash{top} 394 } else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"] 395 for _, rawTopic := range topics { 396 if rawTopic == nil { 397 args.Topics[i] = append(args.Topics[i], common.Hash{}) 398 } else if topic, ok := rawTopic.(string); ok { 399 parsed, err := topicConverter(topic) 400 if err != nil { 401 return err 402 } 403 args.Topics[i] = append(args.Topics[i], parsed) 404 } else { 405 return fmt.Errorf("invalid topic(s)") 406 } 407 } 408 } else { 409 return fmt.Errorf("invalid topic(s)") 410 } 411 } 412 } 413 414 return nil 415 } 416 417 // NewFilter creates a new filter and returns the filter id. It can be uses to retrieve logs. 418 func (s *PublicFilterAPI) NewFilter(args NewFilterArgs) (string, error) { 419 externalId, err := newFilterId() 420 if err != nil { 421 return "", err 422 } 423 424 var id int 425 if len(args.Addresses) > 0 { 426 id, err = s.newLogFilter(args.FromBlock.Int64(), args.ToBlock.Int64(), args.Addresses, args.Topics, nil) 427 } else { 428 id, err = s.newLogFilter(args.FromBlock.Int64(), args.ToBlock.Int64(), nil, args.Topics, nil) 429 } 430 if err != nil { 431 return "", err 432 } 433 434 s.filterMapMu.Lock() 435 s.filterMapping[externalId] = id 436 s.filterMapMu.Unlock() 437 438 return externalId, nil 439 } 440 441 // GetLogs returns the logs matching the given argument. 442 func (s *PublicFilterAPI) GetLogs(args NewFilterArgs) []vmlog { 443 filter := New(s.chainDb) 444 filter.SetBeginBlock(args.FromBlock.Int64()) 445 filter.SetEndBlock(args.ToBlock.Int64()) 446 filter.SetAddresses(args.Addresses) 447 filter.SetTopics(args.Topics) 448 449 return toRPCLogs(filter.Find(), false) 450 } 451 452 // UninstallFilter removes the filter with the given filter id. 453 func (s *PublicFilterAPI) UninstallFilter(filterId string) bool { 454 s.filterManager.Lock() 455 defer s.filterManager.Unlock() 456 457 s.filterMapMu.Lock() 458 id, ok := s.filterMapping[filterId] 459 if !ok { 460 s.filterMapMu.Unlock() 461 return false 462 } 463 delete(s.filterMapping, filterId) 464 s.filterMapMu.Unlock() 465 466 s.filterManager.Remove(id) 467 468 s.logMu.Lock() 469 if _, ok := s.logQueue[id]; ok { 470 delete(s.logQueue, id) 471 s.logMu.Unlock() 472 return true 473 } 474 s.logMu.Unlock() 475 476 s.blockMu.Lock() 477 if _, ok := s.blockQueue[id]; ok { 478 delete(s.blockQueue, id) 479 s.blockMu.Unlock() 480 return true 481 } 482 s.blockMu.Unlock() 483 484 s.transactionMu.Lock() 485 if _, ok := s.transactionQueue[id]; ok { 486 delete(s.transactionQueue, id) 487 s.transactionMu.Unlock() 488 return true 489 } 490 s.transactionMu.Unlock() 491 492 return false 493 } 494 495 // getFilterType is a helper utility that determine the type of filter for the given filter id. 496 func (s *PublicFilterAPI) getFilterType(id int) byte { 497 if _, ok := s.blockQueue[id]; ok { 498 return blockFilterTy 499 } else if _, ok := s.transactionQueue[id]; ok { 500 return transactionFilterTy 501 } else if _, ok := s.logQueue[id]; ok { 502 return logFilterTy 503 } 504 505 return unknownFilterTy 506 } 507 508 // blockFilterChanged returns a collection of block hashes for the block filter with the given id. 509 func (s *PublicFilterAPI) blockFilterChanged(id int) []common.Hash { 510 s.blockMu.Lock() 511 defer s.blockMu.Unlock() 512 513 if s.blockQueue[id] != nil { 514 return s.blockQueue[id].get() 515 } 516 return nil 517 } 518 519 // transactionFilterChanged returns a collection of transaction hashes for the pending 520 // transaction filter with the given id. 521 func (s *PublicFilterAPI) transactionFilterChanged(id int) []common.Hash { 522 s.blockMu.Lock() 523 defer s.blockMu.Unlock() 524 525 if s.transactionQueue[id] != nil { 526 return s.transactionQueue[id].get() 527 } 528 return nil 529 } 530 531 // logFilterChanged returns a collection of logs for the log filter with the given id. 532 func (s *PublicFilterAPI) logFilterChanged(id int) []vmlog { 533 s.logMu.Lock() 534 defer s.logMu.Unlock() 535 536 if s.logQueue[id] != nil { 537 return s.logQueue[id].get() 538 } 539 return nil 540 } 541 542 // GetFilterLogs returns the logs for the filter with the given id. 543 func (s *PublicFilterAPI) GetFilterLogs(filterId string) []vmlog { 544 s.filterMapMu.RLock() 545 id, ok := s.filterMapping[filterId] 546 s.filterMapMu.RUnlock() 547 if !ok { 548 return toRPCLogs(nil, false) 549 } 550 551 if filter := s.filterManager.Get(id); filter != nil { 552 return toRPCLogs(filter.Find(), false) 553 } 554 555 return toRPCLogs(nil, false) 556 } 557 558 // GetFilterChanges returns the logs for the filter with the given id since last time is was called. 559 // This can be used for polling. 560 func (s *PublicFilterAPI) GetFilterChanges(filterId string) interface{} { 561 s.filterMapMu.RLock() 562 id, ok := s.filterMapping[filterId] 563 s.filterMapMu.RUnlock() 564 565 if !ok { // filter not found 566 return []interface{}{} 567 } 568 569 switch s.getFilterType(id) { 570 case blockFilterTy: 571 return returnHashes(s.blockFilterChanged(id)) 572 case transactionFilterTy: 573 return returnHashes(s.transactionFilterChanged(id)) 574 case logFilterTy: 575 return s.logFilterChanged(id) 576 } 577 578 return []interface{}{} 579 } 580 581 type vmlog struct { 582 *vm.Log 583 Removed bool `json:"removed"` 584 } 585 586 type logQueue struct { 587 mu sync.Mutex 588 589 logs []vmlog 590 timeout time.Time 591 } 592 593 func (l *logQueue) add(logs ...vmlog) { 594 l.mu.Lock() 595 defer l.mu.Unlock() 596 597 l.logs = append(l.logs, logs...) 598 } 599 600 func (l *logQueue) get() []vmlog { 601 l.mu.Lock() 602 defer l.mu.Unlock() 603 604 l.timeout = time.Now() 605 tmp := l.logs 606 l.logs = nil 607 return tmp 608 } 609 610 type hashQueue struct { 611 mu sync.Mutex 612 613 hashes []common.Hash 614 timeout time.Time 615 } 616 617 func (l *hashQueue) add(hashes ...common.Hash) { 618 l.mu.Lock() 619 defer l.mu.Unlock() 620 621 l.hashes = append(l.hashes, hashes...) 622 } 623 624 func (l *hashQueue) get() []common.Hash { 625 l.mu.Lock() 626 defer l.mu.Unlock() 627 628 l.timeout = time.Now() 629 tmp := l.hashes 630 l.hashes = nil 631 return tmp 632 } 633 634 // newFilterId generates a new random filter identifier that can be exposed to the outer world. By publishing random 635 // identifiers it is not feasible for DApp's to guess filter id's for other DApp's and uninstall or poll for them 636 // causing the affected DApp to miss data. 637 func newFilterId() (string, error) { 638 var subid [16]byte 639 n, _ := rand.Read(subid[:]) 640 if n != 16 { 641 return "", errors.New("Unable to generate filter id") 642 } 643 return "0x" + hex.EncodeToString(subid[:]), nil 644 } 645 646 // toRPCLogs is a helper that will convert a vm.Logs array to an structure which 647 // can hold additional information about the logs such as whether it was deleted. 648 // Additionally when nil is given it will by default instead create an empty slice 649 // instead. This is required by the RPC specification. 650 func toRPCLogs(logs vm.Logs, removed bool) []vmlog { 651 convertedLogs := make([]vmlog, len(logs)) 652 for i, log := range logs { 653 convertedLogs[i] = vmlog{Log: log, Removed: removed} 654 } 655 return convertedLogs 656 } 657 658 // returnHashes is a helper that will return an empty hash array case the given hash array is nil, otherwise is will 659 // return the given hashes. The RPC interfaces defines that always an array is returned. 660 func returnHashes(hashes []common.Hash) []common.Hash { 661 if hashes == nil { 662 return []common.Hash{} 663 } 664 return hashes 665 }