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