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