github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/eth/filters/api.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 12:09:38</date> 10 //</624342635041001472> 11 12 13 package filters 14 15 import ( 16 "context" 17 "encoding/json" 18 "errors" 19 "fmt" 20 "math/big" 21 "sync" 22 "time" 23 24 ethereum "github.com/ethereum/go-ethereum" 25 "github.com/ethereum/go-ethereum/common" 26 "github.com/ethereum/go-ethereum/common/hexutil" 27 "github.com/ethereum/go-ethereum/core/types" 28 "github.com/ethereum/go-ethereum/ethdb" 29 "github.com/ethereum/go-ethereum/event" 30 "github.com/ethereum/go-ethereum/rpc" 31 ) 32 33 var ( 34 deadline = 5 * time.Minute //如果在截止日期内未对某个筛选器进行轮询,则认为该筛选器处于非活动状态 35 ) 36 37 //filter是一个在filter类型上保存元信息的helper结构 38 //以及事件系统中的关联订阅。 39 type filter struct { 40 typ Type 41 deadline *time.Timer //截止时间触发时,筛选器不活动 42 hashes []common.Hash 43 crit FilterCriteria 44 logs []*types.Log 45 s *Subscription //事件系统中的关联订阅 46 } 47 48 //publicfilterapi提供创建和管理过滤器的支持。这将允许外部客户端检索 49 //与以太坊协议相关的信息,如ALS块、事务和日志。 50 type PublicFilterAPI struct { 51 backend Backend 52 mux *event.TypeMux 53 quit chan struct{} 54 chainDb ethdb.Database 55 events *EventSystem 56 filtersMu sync.Mutex 57 filters map[rpc.ID]*filter 58 } 59 60 //new publicfilterapi返回新的publicfilterapi实例。 61 func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { 62 api := &PublicFilterAPI{ 63 backend: backend, 64 mux: backend.EventMux(), 65 chainDb: backend.ChainDb(), 66 events: NewEventSystem(backend.EventMux(), backend, lightMode), 67 filters: make(map[rpc.ID]*filter), 68 } 69 go api.timeoutLoop() 70 71 return api 72 } 73 74 //TimeoutLoop每5分钟运行一次,并删除最近未使用的筛选器。 75 //TT在创建API时启动。 76 func (api *PublicFilterAPI) timeoutLoop() { 77 ticker := time.NewTicker(5 * time.Minute) 78 for { 79 <-ticker.C 80 api.filtersMu.Lock() 81 for id, f := range api.filters { 82 select { 83 case <-f.deadline.C: 84 f.s.Unsubscribe() 85 delete(api.filters, id) 86 default: 87 continue 88 } 89 } 90 api.filtersMu.Unlock() 91 } 92 } 93 94 //NewPendingtTransactionFilter创建一个筛选器,用于获取挂起的事务哈希 95 //当事务进入挂起状态时。 96 // 97 //它是筛选器包的一部分,因为此筛选器可以通过 98 //“eth”getfilterchanges“轮询方法,也用于日志筛选器。 99 // 100 //https://github.com/ethereum/wiki/wiki/json-rpc_eth_newpendingtransactionfilter 101 func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { 102 var ( 103 pendingTxs = make(chan []common.Hash) 104 pendingTxSub = api.events.SubscribePendingTxs(pendingTxs) 105 ) 106 107 api.filtersMu.Lock() 108 api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub} 109 api.filtersMu.Unlock() 110 111 go func() { 112 for { 113 select { 114 case ph := <-pendingTxs: 115 api.filtersMu.Lock() 116 if f, found := api.filters[pendingTxSub.ID]; found { 117 f.hashes = append(f.hashes, ph...) 118 } 119 api.filtersMu.Unlock() 120 case <-pendingTxSub.Err(): 121 api.filtersMu.Lock() 122 delete(api.filters, pendingTxSub.ID) 123 api.filtersMu.Unlock() 124 return 125 } 126 } 127 }() 128 129 return pendingTxSub.ID 130 } 131 132 //NewPendingtTransactions创建每次事务触发的订阅 133 //进入事务池,并从该节点管理的事务之一签名。 134 func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) { 135 notifier, supported := rpc.NotifierFromContext(ctx) 136 if !supported { 137 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 138 } 139 140 rpcSub := notifier.CreateSubscription() 141 142 go func() { 143 txHashes := make(chan []common.Hash, 128) 144 pendingTxSub := api.events.SubscribePendingTxs(txHashes) 145 146 for { 147 select { 148 case hashes := <-txHashes: 149 //要保持原始行为,请在一个通知中发送单个Tx哈希。 150 //TODO(RJL493456442)在一个通知中发送一批Tx哈希 151 for _, h := range hashes { 152 notifier.Notify(rpcSub.ID, h) 153 } 154 case <-rpcSub.Err(): 155 pendingTxSub.Unsubscribe() 156 return 157 case <-notifier.Closed(): 158 pendingTxSub.Unsubscribe() 159 return 160 } 161 } 162 }() 163 164 return rpcSub, nil 165 } 166 167 //newblockfilter创建一个过滤器,用于获取导入到链中的块。 168 //它是筛选包的一部分,因为轮询与ethgetfilterchanges一起进行。 169 // 170 //https://github.com/ethereum/wiki/wiki/json-rpc eth newblockfilter 171 func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { 172 var ( 173 headers = make(chan *types.Header) 174 headerSub = api.events.SubscribeNewHeads(headers) 175 ) 176 177 api.filtersMu.Lock() 178 api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: headerSub} 179 api.filtersMu.Unlock() 180 181 go func() { 182 for { 183 select { 184 case h := <-headers: 185 api.filtersMu.Lock() 186 if f, found := api.filters[headerSub.ID]; found { 187 f.hashes = append(f.hashes, h.Hash()) 188 } 189 api.filtersMu.Unlock() 190 case <-headerSub.Err(): 191 api.filtersMu.Lock() 192 delete(api.filters, headerSub.ID) 193 api.filtersMu.Unlock() 194 return 195 } 196 } 197 }() 198 199 return headerSub.ID 200 } 201 202 //每当新(header)块附加到链时,newheads都会发送通知。 203 func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { 204 notifier, supported := rpc.NotifierFromContext(ctx) 205 if !supported { 206 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 207 } 208 209 rpcSub := notifier.CreateSubscription() 210 211 go func() { 212 headers := make(chan *types.Header) 213 headersSub := api.events.SubscribeNewHeads(headers) 214 215 for { 216 select { 217 case h := <-headers: 218 notifier.Notify(rpcSub.ID, h) 219 case <-rpcSub.Err(): 220 headersSub.Unsubscribe() 221 return 222 case <-notifier.Closed(): 223 headersSub.Unsubscribe() 224 return 225 } 226 } 227 }() 228 229 return rpcSub, nil 230 } 231 232 //日志创建一个订阅,该订阅为所有符合给定筛选条件的新日志激发。 233 func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) { 234 notifier, supported := rpc.NotifierFromContext(ctx) 235 if !supported { 236 return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported 237 } 238 239 var ( 240 rpcSub = notifier.CreateSubscription() 241 matchedLogs = make(chan []*types.Log) 242 ) 243 244 logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), matchedLogs) 245 if err != nil { 246 return nil, err 247 } 248 249 go func() { 250 251 for { 252 select { 253 case logs := <-matchedLogs: 254 for _, log := range logs { 255 notifier.Notify(rpcSub.ID, &log) 256 } 257 case <-rpcSub.Err(): //客户端发送取消订阅请求 258 logsSub.Unsubscribe() 259 return 260 case <-notifier.Closed(): //连接已断开 261 logsSub.Unsubscribe() 262 return 263 } 264 } 265 }() 266 267 return rpcSub, nil 268 } 269 270 //FilterCriteria表示创建新筛选器的请求。 271 //与ethereum.filterquery相同,但使用unmashaljson()方法。 272 type FilterCriteria ethereum.FilterQuery 273 274 //new filter创建新的筛选器并返回筛选器ID。它可以是 275 //用于在状态更改时检索日志。此方法不能 276 //用于获取已存储在状态中的日志。 277 // 278 //“从”和“到”块的默认条件为“最新”。 279 //使用“最新”作为块号将返回已开采块的日志。 280 //使用“挂起”作为块号返回尚未挖掘(挂起)块的日志。 281 //如果日志被删除(链ReRoG),则返回以前返回的日志。 282 //再次,但将移除的属性设置为true。 283 // 284 //如果“fromblock”>toblock,则返回错误。 285 // 286 //https://github.com/ethereum/wiki/wiki/json-rpc_eth_newfilter 287 func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { 288 logs := make(chan []*types.Log) 289 logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), logs) 290 if err != nil { 291 return rpc.ID(""), err 292 } 293 294 api.filtersMu.Lock() 295 api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*types.Log, 0), s: logsSub} 296 api.filtersMu.Unlock() 297 298 go func() { 299 for { 300 select { 301 case l := <-logs: 302 api.filtersMu.Lock() 303 if f, found := api.filters[logsSub.ID]; found { 304 f.logs = append(f.logs, l...) 305 } 306 api.filtersMu.Unlock() 307 case <-logsSub.Err(): 308 api.filtersMu.Lock() 309 delete(api.filters, logsSub.ID) 310 api.filtersMu.Unlock() 311 return 312 } 313 } 314 }() 315 316 return logsSub.ID, nil 317 } 318 319 //GetLogs返回与存储在状态中的给定参数匹配的日志。 320 // 321 //https://github.com/ethereum/wiki/wiki/json-rpc eth getlogs 322 func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) { 323 var filter *Filter 324 if crit.BlockHash != nil { 325 //请求块筛选器,构造一个单镜头筛选器 326 filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics) 327 } else { 328 //将RPC块编号转换为内部表示形式 329 begin := rpc.LatestBlockNumber.Int64() 330 if crit.FromBlock != nil { 331 begin = crit.FromBlock.Int64() 332 } 333 end := rpc.LatestBlockNumber.Int64() 334 if crit.ToBlock != nil { 335 end = crit.ToBlock.Int64() 336 } 337 //构造范围过滤器 338 filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics) 339 } 340 //运行过滤器并返回所有日志 341 logs, err := filter.Logs(ctx) 342 if err != nil { 343 return nil, err 344 } 345 return returnLogs(logs), err 346 } 347 348 //uninstallfilter删除具有给定筛选器ID的筛选器。 349 // 350 //https://github.com/ethereum/wiki/wiki/json-rpc_eth_卸载过滤器 351 func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { 352 api.filtersMu.Lock() 353 f, found := api.filters[id] 354 if found { 355 delete(api.filters, id) 356 } 357 api.filtersMu.Unlock() 358 if found { 359 f.s.Unsubscribe() 360 } 361 362 return found 363 } 364 365 //GetFilterLogs返回具有给定ID的筛选器的日志。 366 //如果找不到筛选器,则返回一个空的日志数组。 367 // 368 //https://github.com/ethereum/wiki/wiki/json-rpc_eth_getfilterlogs 369 func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) { 370 api.filtersMu.Lock() 371 f, found := api.filters[id] 372 api.filtersMu.Unlock() 373 374 if !found || f.typ != LogsSubscription { 375 return nil, fmt.Errorf("filter not found") 376 } 377 378 var filter *Filter 379 if f.crit.BlockHash != nil { 380 //请求块筛选器,构造一个单镜头筛选器 381 filter = NewBlockFilter(api.backend, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics) 382 } else { 383 //将RPC块编号转换为内部表示形式 384 begin := rpc.LatestBlockNumber.Int64() 385 if f.crit.FromBlock != nil { 386 begin = f.crit.FromBlock.Int64() 387 } 388 end := rpc.LatestBlockNumber.Int64() 389 if f.crit.ToBlock != nil { 390 end = f.crit.ToBlock.Int64() 391 } 392 //构造范围过滤器 393 filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics) 394 } 395 //运行过滤器并返回所有日志 396 logs, err := filter.Logs(ctx) 397 if err != nil { 398 return nil, err 399 } 400 return returnLogs(logs), nil 401 } 402 403 //GetFilterChanges返回具有给定ID的筛选器的日志,自 404 //上次打电话的时候。这可以用于轮询。 405 // 406 //对于挂起的事务和块筛选器,结果是[]common.hash。 407 //(挂起)日志筛选器返回[]日志。 408 // 409 //https://github.com/ethereum/wiki/wiki/json-rpc_eth_getfilterchanges 410 func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { 411 api.filtersMu.Lock() 412 defer api.filtersMu.Unlock() 413 414 if f, found := api.filters[id]; found { 415 if !f.deadline.Stop() { 416 //计时器已过期,但在超时循环中尚未删除筛选器 417 //接收计时器值并重置计时器 418 <-f.deadline.C 419 } 420 f.deadline.Reset(deadline) 421 422 switch f.typ { 423 case PendingTransactionsSubscription, BlocksSubscription: 424 hashes := f.hashes 425 f.hashes = nil 426 return returnHashes(hashes), nil 427 case LogsSubscription: 428 logs := f.logs 429 f.logs = nil 430 return returnLogs(logs), nil 431 } 432 } 433 434 return []interface{}{}, fmt.Errorf("filter not found") 435 } 436 437 //ReturnHashes是一个助手,在给定哈希数组为零的情况下将返回空哈希数组, 438 //否则返回给定的哈希数组。 439 func returnHashes(hashes []common.Hash) []common.Hash { 440 if hashes == nil { 441 return []common.Hash{} 442 } 443 return hashes 444 } 445 446 //ReturnLogs是一个帮助程序,当给定的日志数组为零时,它将返回空的日志数组。 447 //否则返回给定的logs数组。 448 func returnLogs(logs []*types.Log) []*types.Log { 449 if logs == nil { 450 return []*types.Log{} 451 } 452 return logs 453 } 454 455 //用给定的数据取消标记JSON集合*参数字段。 456 func (args *FilterCriteria) UnmarshalJSON(data []byte) error { 457 type input struct { 458 BlockHash *common.Hash `json:"blockHash"` 459 FromBlock *rpc.BlockNumber `json:"fromBlock"` 460 ToBlock *rpc.BlockNumber `json:"toBlock"` 461 Addresses interface{} `json:"address"` 462 Topics []interface{} `json:"topics"` 463 } 464 465 var raw input 466 if err := json.Unmarshal(data, &raw); err != nil { 467 return err 468 } 469 470 if raw.BlockHash != nil { 471 if raw.FromBlock != nil || raw.ToBlock != nil { 472 //blockhash与fromblock/toblock条件互斥 473 return fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock, choose one or the other") 474 } 475 args.BlockHash = raw.BlockHash 476 } else { 477 if raw.FromBlock != nil { 478 args.FromBlock = big.NewInt(raw.FromBlock.Int64()) 479 } 480 481 if raw.ToBlock != nil { 482 args.ToBlock = big.NewInt(raw.ToBlock.Int64()) 483 } 484 } 485 486 args.Addresses = []common.Address{} 487 488 if raw.Addresses != nil { 489 //原始地址可以包含单个地址或地址数组 490 switch rawAddr := raw.Addresses.(type) { 491 case []interface{}: 492 for i, addr := range rawAddr { 493 if strAddr, ok := addr.(string); ok { 494 addr, err := decodeAddress(strAddr) 495 if err != nil { 496 return fmt.Errorf("invalid address at index %d: %v", i, err) 497 } 498 args.Addresses = append(args.Addresses, addr) 499 } else { 500 return fmt.Errorf("non-string address at index %d", i) 501 } 502 } 503 case string: 504 addr, err := decodeAddress(rawAddr) 505 if err != nil { 506 return fmt.Errorf("invalid address: %v", err) 507 } 508 args.Addresses = []common.Address{addr} 509 default: 510 return errors.New("invalid addresses in query") 511 } 512 } 513 514 //主题是由字符串和/或字符串数组组成的数组。 515 //JSON空值转换为common.hash并被筛选器管理器忽略。 516 if len(raw.Topics) > 0 { 517 args.Topics = make([][]common.Hash, len(raw.Topics)) 518 for i, t := range raw.Topics { 519 switch topic := t.(type) { 520 case nil: 521 //匹配日志时忽略主题 522 523 case string: 524 //匹配特定主题 525 top, err := decodeTopic(topic) 526 if err != nil { 527 return err 528 } 529 args.Topics[i] = []common.Hash{top} 530 531 case []interface{}: 532 //或案例,例如[空,“topic0”,“topic1”] 533 for _, rawTopic := range topic { 534 if rawTopic == nil { 535 //空组件,全部匹配 536 args.Topics[i] = nil 537 break 538 } 539 if topic, ok := rawTopic.(string); ok { 540 parsed, err := decodeTopic(topic) 541 if err != nil { 542 return err 543 } 544 args.Topics[i] = append(args.Topics[i], parsed) 545 } else { 546 return fmt.Errorf("invalid topic(s)") 547 } 548 } 549 default: 550 return fmt.Errorf("invalid topic(s)") 551 } 552 } 553 } 554 555 return nil 556 } 557 558 func decodeAddress(s string) (common.Address, error) { 559 b, err := hexutil.Decode(s) 560 if err == nil && len(b) != common.AddressLength { 561 err = fmt.Errorf("hex has invalid length %d after decoding; expected %d for address", len(b), common.AddressLength) 562 } 563 return common.BytesToAddress(b), err 564 } 565 566 func decodeTopic(s string) (common.Hash, error) { 567 b, err := hexutil.Decode(s) 568 if err == nil && len(b) != common.HashLength { 569 err = fmt.Errorf("hex has invalid length %d after decoding; expected %d for topic", len(b), common.HashLength) 570 } 571 return common.BytesToHash(b), err 572 } 573