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  }