| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // This file is part of the go-ethereum library. | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // GNU Lesser General Public License for more details. | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | package filters | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2017-03-22 18:20:33 +01:00
										 |  |  | 	"context" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	"math/big" | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-05 12:39:24 +02:00
										 |  |  | 	ethereum "github.com/ethereum/go-ethereum" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/common/hexutil" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/core/types" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/event" | 
					
						
							| 
									
										
										
										
											2015-12-16 10:58:01 +01:00
										 |  |  | 	"github.com/ethereum/go-ethereum/rpc" | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var ( | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	deadline = 5 * time.Minute // consider a filter inactive if it has not been polled for within deadline | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // filter is a helper struct that holds meta information over the filter type | 
					
						
							|  |  |  | // and associated subscription in the event system. | 
					
						
							|  |  |  | type filter struct { | 
					
						
							|  |  |  | 	typ      Type | 
					
						
							|  |  |  | 	deadline *time.Timer // filter is inactiv when deadline triggers | 
					
						
							|  |  |  | 	hashes   []common.Hash | 
					
						
							|  |  |  | 	crit     FilterCriteria | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 	logs     []*types.Log | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	s        *Subscription // associated subscription in event system | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-03-15 11:27:49 -07:00
										 |  |  | // PublicFilterAPI offers support to create and manage filters. This will allow external clients to retrieve various | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | // information related to the Ethereum protocol such als blocks, transactions and logs. | 
					
						
							|  |  |  | type PublicFilterAPI struct { | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	backend   Backend | 
					
						
							|  |  |  | 	mux       *event.TypeMux | 
					
						
							|  |  |  | 	quit      chan struct{} | 
					
						
							|  |  |  | 	chainDb   ethdb.Database | 
					
						
							|  |  |  | 	events    *EventSystem | 
					
						
							|  |  |  | 	filtersMu sync.Mutex | 
					
						
							|  |  |  | 	filters   map[rpc.ID]*filter | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewPublicFilterAPI returns a new PublicFilterAPI instance. | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | func NewPublicFilterAPI(backend Backend, lightMode bool) *PublicFilterAPI { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api := &PublicFilterAPI{ | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 		backend: backend, | 
					
						
							|  |  |  | 		mux:     backend.EventMux(), | 
					
						
							|  |  |  | 		chainDb: backend.ChainDb(), | 
					
						
							|  |  |  | 		events:  NewEventSystem(backend.EventMux(), backend, lightMode), | 
					
						
							|  |  |  | 		filters: make(map[rpc.ID]*filter), | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	go api.timeoutLoop() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return api | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // timeoutLoop runs every 5 minutes and deletes filters that have not been recently used. | 
					
						
							|  |  |  | // Tt is started when the api is created. | 
					
						
							|  |  |  | func (api *PublicFilterAPI) timeoutLoop() { | 
					
						
							|  |  |  | 	ticker := time.NewTicker(5 * time.Minute) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	for { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		<-ticker.C | 
					
						
							|  |  |  | 		api.filtersMu.Lock() | 
					
						
							|  |  |  | 		for id, f := range api.filters { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case <-f.deadline.C: | 
					
						
							|  |  |  | 				f.s.Unsubscribe() | 
					
						
							|  |  |  | 				delete(api.filters, id) | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				continue | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		api.filtersMu.Unlock() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // NewPendingTransactionFilter creates a filter that fetches pending transaction hashes | 
					
						
							|  |  |  | // as transactions enter the pending state. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2018-04-04 18:25:02 +08:00
										 |  |  | // It is part of the filter package because this filter can be used through the | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // `eth_getFilterChanges` polling method that is also used for log filters. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter | 
					
						
							|  |  |  | func (api *PublicFilterAPI) NewPendingTransactionFilter() rpc.ID { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		pendingTxs   = make(chan common.Hash) | 
					
						
							|  |  |  | 		pendingTxSub = api.events.SubscribePendingTxEvents(pendingTxs) | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Lock() | 
					
						
							|  |  |  | 	api.filters[pendingTxSub.ID] = &filter{typ: PendingTransactionsSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: pendingTxSub} | 
					
						
							|  |  |  | 	api.filtersMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case ph := <-pendingTxs: | 
					
						
							|  |  |  | 				api.filtersMu.Lock() | 
					
						
							|  |  |  | 				if f, found := api.filters[pendingTxSub.ID]; found { | 
					
						
							|  |  |  | 					f.hashes = append(f.hashes, ph) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 				api.filtersMu.Unlock() | 
					
						
							|  |  |  | 			case <-pendingTxSub.Err(): | 
					
						
							|  |  |  | 				api.filtersMu.Lock() | 
					
						
							|  |  |  | 				delete(api.filters, pendingTxSub.ID) | 
					
						
							|  |  |  | 				api.filtersMu.Unlock() | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	return pendingTxSub.ID | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // NewPendingTransactions creates a subscription that is triggered each time a transaction | 
					
						
							|  |  |  | // enters the transaction pool and was signed from one of the transactions this nodes manages. | 
					
						
							|  |  |  | func (api *PublicFilterAPI) NewPendingTransactions(ctx context.Context) (*rpc.Subscription, error) { | 
					
						
							|  |  |  | 	notifier, supported := rpc.NotifierFromContext(ctx) | 
					
						
							|  |  |  | 	if !supported { | 
					
						
							|  |  |  | 		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	rpcSub := notifier.CreateSubscription() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		txHashes := make(chan common.Hash) | 
					
						
							|  |  |  | 		pendingTxSub := api.events.SubscribePendingTxEvents(txHashes) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case h := <-txHashes: | 
					
						
							|  |  |  | 				notifier.Notify(rpcSub.ID, h) | 
					
						
							|  |  |  | 			case <-rpcSub.Err(): | 
					
						
							|  |  |  | 				pendingTxSub.Unsubscribe() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			case <-notifier.Closed(): | 
					
						
							|  |  |  | 				pendingTxSub.Unsubscribe() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2016-02-13 01:40:44 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	return rpcSub, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // NewBlockFilter creates a filter that fetches blocks that are imported into the chain. | 
					
						
							|  |  |  | // It is part of the filter package since polling goes with eth_getFilterChanges. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newblockfilter | 
					
						
							|  |  |  | func (api *PublicFilterAPI) NewBlockFilter() rpc.ID { | 
					
						
							|  |  |  | 	var ( | 
					
						
							|  |  |  | 		headers   = make(chan *types.Header) | 
					
						
							|  |  |  | 		headerSub = api.events.SubscribeNewHeads(headers) | 
					
						
							|  |  |  | 	) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Lock() | 
					
						
							|  |  |  | 	api.filters[headerSub.ID] = &filter{typ: BlocksSubscription, deadline: time.NewTimer(deadline), hashes: make([]common.Hash, 0), s: headerSub} | 
					
						
							|  |  |  | 	api.filtersMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case h := <-headers: | 
					
						
							|  |  |  | 				api.filtersMu.Lock() | 
					
						
							|  |  |  | 				if f, found := api.filters[headerSub.ID]; found { | 
					
						
							|  |  |  | 					f.hashes = append(f.hashes, h.Hash()) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				api.filtersMu.Unlock() | 
					
						
							|  |  |  | 			case <-headerSub.Err(): | 
					
						
							|  |  |  | 				api.filtersMu.Lock() | 
					
						
							|  |  |  | 				delete(api.filters, headerSub.ID) | 
					
						
							|  |  |  | 				api.filtersMu.Unlock() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	}() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return headerSub.ID | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewHeads send a notification each time a new (header) block is appended to the chain. | 
					
						
							|  |  |  | func (api *PublicFilterAPI) NewHeads(ctx context.Context) (*rpc.Subscription, error) { | 
					
						
							|  |  |  | 	notifier, supported := rpc.NotifierFromContext(ctx) | 
					
						
							|  |  |  | 	if !supported { | 
					
						
							|  |  |  | 		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	rpcSub := notifier.CreateSubscription() | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	go func() { | 
					
						
							|  |  |  | 		headers := make(chan *types.Header) | 
					
						
							|  |  |  | 		headersSub := api.events.SubscribeNewHeads(headers) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case h := <-headers: | 
					
						
							|  |  |  | 				notifier.Notify(rpcSub.ID, h) | 
					
						
							|  |  |  | 			case <-rpcSub.Err(): | 
					
						
							|  |  |  | 				headersSub.Unsubscribe() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			case <-notifier.Closed(): | 
					
						
							|  |  |  | 				headersSub.Unsubscribe() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2016-06-17 09:53:54 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	return rpcSub, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // Logs creates a subscription that fires for all new log that match the given filter criteria. | 
					
						
							|  |  |  | func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc.Subscription, error) { | 
					
						
							|  |  |  | 	notifier, supported := rpc.NotifierFromContext(ctx) | 
					
						
							|  |  |  | 	if !supported { | 
					
						
							|  |  |  | 		return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported | 
					
						
							| 
									
										
										
										
											2016-02-13 01:40:44 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	var ( | 
					
						
							|  |  |  | 		rpcSub      = notifier.CreateSubscription() | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 		matchedLogs = make(chan []*types.Log) | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-05 12:39:24 +02:00
										 |  |  | 	logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), matchedLogs) | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	go func() { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case logs := <-matchedLogs: | 
					
						
							|  |  |  | 				for _, log := range logs { | 
					
						
							|  |  |  | 					notifier.Notify(rpcSub.ID, &log) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			case <-rpcSub.Err(): // client send an unsubscribe request | 
					
						
							|  |  |  | 				logsSub.Unsubscribe() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			case <-notifier.Closed(): // connection dropped | 
					
						
							|  |  |  | 				logsSub.Unsubscribe() | 
					
						
							|  |  |  | 				return | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	return rpcSub, nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // FilterCriteria represents a request to create a new filter. | 
					
						
							| 
									
										
										
										
											2018-01-05 12:39:24 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // TODO(karalabe): Kill this in favor of ethereum.FilterQuery. | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | type FilterCriteria struct { | 
					
						
							|  |  |  | 	FromBlock *big.Int | 
					
						
							|  |  |  | 	ToBlock   *big.Int | 
					
						
							|  |  |  | 	Addresses []common.Address | 
					
						
							|  |  |  | 	Topics    [][]common.Hash | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // NewFilter creates a new filter and returns the filter id. It can be | 
					
						
							|  |  |  | // used to retrieve logs when the state changes. This method cannot be | 
					
						
							|  |  |  | // used to fetch logs that are already stored in the state. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | // Default criteria for the from and to block are "latest". | 
					
						
							|  |  |  | // Using "latest" as block number will return logs for mined blocks. | 
					
						
							|  |  |  | // Using "pending" as block number returns logs for not yet mined (pending) blocks. | 
					
						
							|  |  |  | // In case logs are removed (chain reorg) previously returned logs are returned | 
					
						
							|  |  |  | // again but with the removed property set to true. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // In case "fromBlock" > "toBlock" an error is returned. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) { | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 	logs := make(chan []*types.Log) | 
					
						
							| 
									
										
										
										
											2018-01-05 12:39:24 +02:00
										 |  |  | 	logsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), logs) | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return rpc.ID(""), err | 
					
						
							| 
									
										
										
										
											2016-02-13 01:40:44 +01:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Lock() | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 	api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*types.Log, 0), s: logsSub} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			select { | 
					
						
							|  |  |  | 			case l := <-logs: | 
					
						
							|  |  |  | 				api.filtersMu.Lock() | 
					
						
							|  |  |  | 				if f, found := api.filters[logsSub.ID]; found { | 
					
						
							|  |  |  | 					f.logs = append(f.logs, l...) | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				api.filtersMu.Unlock() | 
					
						
							|  |  |  | 			case <-logsSub.Err(): | 
					
						
							|  |  |  | 				api.filtersMu.Lock() | 
					
						
							|  |  |  | 				delete(api.filters, logsSub.ID) | 
					
						
							|  |  |  | 				api.filtersMu.Unlock() | 
					
						
							|  |  |  | 				return | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	}() | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	return logsSub.ID, nil | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // GetLogs returns logs matching the given argument that are stored within the state. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) { | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	// Convert the RPC block numbers into internal representations | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	if crit.FromBlock == nil { | 
					
						
							|  |  |  | 		crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if crit.ToBlock == nil { | 
					
						
							|  |  |  | 		crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64()) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	// Create and run the filter to get all the logs | 
					
						
							| 
									
										
										
										
											2017-08-18 21:52:20 +02:00
										 |  |  | 	filter := New(api.backend, crit.FromBlock.Int64(), crit.ToBlock.Int64(), crit.Addresses, crit.Topics) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	logs, err := filter.Logs(ctx) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-01-13 19:35:48 +01:00
										 |  |  | 	return returnLogs(logs), err | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // UninstallFilter removes the filter with the given filter id. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_uninstallfilter | 
					
						
							|  |  |  | func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool { | 
					
						
							|  |  |  | 	api.filtersMu.Lock() | 
					
						
							|  |  |  | 	f, found := api.filters[id] | 
					
						
							|  |  |  | 	if found { | 
					
						
							|  |  |  | 		delete(api.filters, id) | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Unlock() | 
					
						
							|  |  |  | 	if found { | 
					
						
							|  |  |  | 		f.s.Unsubscribe() | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	return found | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetFilterLogs returns the logs for the filter with the given id. | 
					
						
							|  |  |  | // If the filter could not be found an empty array of logs is returned. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*types.Log, error) { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Lock() | 
					
						
							|  |  |  | 	f, found := api.filters[id] | 
					
						
							|  |  |  | 	api.filtersMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if !found || f.typ != LogsSubscription { | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 		return nil, fmt.Errorf("filter not found") | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	begin := rpc.LatestBlockNumber.Int64() | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if f.crit.FromBlock != nil { | 
					
						
							| 
									
										
										
										
											2017-08-18 21:52:20 +02:00
										 |  |  | 		begin = f.crit.FromBlock.Int64() | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	end := rpc.LatestBlockNumber.Int64() | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if f.crit.ToBlock != nil { | 
					
						
							| 
									
										
										
										
											2017-08-18 21:52:20 +02:00
										 |  |  | 		end = f.crit.ToBlock.Int64() | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	// Create and run the filter to get all the logs | 
					
						
							| 
									
										
										
										
											2017-08-18 21:52:20 +02:00
										 |  |  | 	filter := New(api.backend, begin, end, f.crit.Addresses, f.crit.Topics) | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | 	logs, err := filter.Logs(ctx) | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return returnLogs(logs), nil | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // GetFilterChanges returns the logs for the filter with the given id since | 
					
						
							| 
									
										
										
										
											2017-08-29 14:13:11 +03:00
										 |  |  | // last time it was called. This can be used for polling. | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // For pending transaction and block filters the result is []common.Hash. | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | // (pending)Log filters return []Log. | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // | 
					
						
							|  |  |  | // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	api.filtersMu.Lock() | 
					
						
							|  |  |  | 	defer api.filtersMu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if f, found := api.filters[id]; found { | 
					
						
							|  |  |  | 		if !f.deadline.Stop() { | 
					
						
							|  |  |  | 			// timer expired but filter is not yet removed in timeout loop | 
					
						
							|  |  |  | 			// receive timer value and reset timer | 
					
						
							|  |  |  | 			<-f.deadline.C | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		f.deadline.Reset(deadline) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch f.typ { | 
					
						
							|  |  |  | 		case PendingTransactionsSubscription, BlocksSubscription: | 
					
						
							|  |  |  | 			hashes := f.hashes | 
					
						
							|  |  |  | 			f.hashes = nil | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 			return returnHashes(hashes), nil | 
					
						
							|  |  |  | 		case LogsSubscription: | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 			logs := f.logs | 
					
						
							|  |  |  | 			f.logs = nil | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 			return returnLogs(logs), nil | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	return []interface{}{}, fmt.Errorf("filter not found") | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // returnHashes is a helper that will return an empty hash array case the given hash array is nil, | 
					
						
							|  |  |  | // otherwise the given hashes array is returned. | 
					
						
							|  |  |  | func returnHashes(hashes []common.Hash) []common.Hash { | 
					
						
							|  |  |  | 	if hashes == nil { | 
					
						
							|  |  |  | 		return []common.Hash{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return hashes | 
					
						
							| 
									
										
										
										
											2016-03-29 15:07:40 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | // returnLogs is a helper that will return an empty log array in case the given logs array is nil, | 
					
						
							|  |  |  | // otherwise the given logs array is returned. | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | func returnLogs(logs []*types.Log) []*types.Log { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	if logs == nil { | 
					
						
							| 
									
										
										
										
											2017-01-05 14:03:50 +01:00
										 |  |  | 		return []*types.Log{} | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return logs | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | // UnmarshalJSON sets *args fields with given data. | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | func (args *FilterCriteria) UnmarshalJSON(data []byte) error { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	type input struct { | 
					
						
							|  |  |  | 		From      *rpc.BlockNumber `json:"fromBlock"` | 
					
						
							|  |  |  | 		ToBlock   *rpc.BlockNumber `json:"toBlock"` | 
					
						
							|  |  |  | 		Addresses interface{}      `json:"address"` | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 		Topics    []interface{}    `json:"topics"` | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	var raw input | 
					
						
							|  |  |  | 	if err := json.Unmarshal(data, &raw); err != nil { | 
					
						
							|  |  |  | 		return err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if raw.From != nil { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		args.FromBlock = big.NewInt(raw.From.Int64()) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 14:59:06 +01:00
										 |  |  | 	if raw.ToBlock != nil { | 
					
						
							| 
									
										
										
										
											2016-07-27 17:47:46 +02:00
										 |  |  | 		args.ToBlock = big.NewInt(raw.ToBlock.Int64()) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	args.Addresses = []common.Address{} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if raw.Addresses != nil { | 
					
						
							|  |  |  | 		// raw.Address can contain a single address or an array of addresses | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 		switch rawAddr := raw.Addresses.(type) { | 
					
						
							|  |  |  | 		case []interface{}: | 
					
						
							|  |  |  | 			for i, addr := range rawAddr { | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				if strAddr, ok := addr.(string); ok { | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 					addr, err := decodeAddress(strAddr) | 
					
						
							|  |  |  | 					if err != nil { | 
					
						
							|  |  |  | 						return fmt.Errorf("invalid address at index %d: %v", i, err) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 					args.Addresses = append(args.Addresses, addr) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				} else { | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 					return fmt.Errorf("non-string address at index %d", i) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 		case string: | 
					
						
							|  |  |  | 			addr, err := decodeAddress(rawAddr) | 
					
						
							|  |  |  | 			if err != nil { | 
					
						
							|  |  |  | 				return fmt.Errorf("invalid address: %v", err) | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 			args.Addresses = []common.Address{addr} | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			return errors.New("invalid addresses in query") | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 	// topics is an array consisting of strings and/or arrays of strings. | 
					
						
							|  |  |  | 	// JSON null values are converted to common.Hash{} and ignored by the filter manager. | 
					
						
							|  |  |  | 	if len(raw.Topics) > 0 { | 
					
						
							|  |  |  | 		args.Topics = make([][]common.Hash, len(raw.Topics)) | 
					
						
							|  |  |  | 		for i, t := range raw.Topics { | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 			switch topic := t.(type) { | 
					
						
							|  |  |  | 			case nil: | 
					
						
							|  |  |  | 				// ignore topic when matching logs | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			case string: | 
					
						
							|  |  |  | 				// match specific topic | 
					
						
							|  |  |  | 				top, err := decodeTopic(topic) | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 				if err != nil { | 
					
						
							|  |  |  | 					return err | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				args.Topics[i] = []common.Hash{top} | 
					
						
							| 
									
										
										
										
											2017-09-27 13:14:52 +03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 			case []interface{}: | 
					
						
							|  |  |  | 				// or case e.g. [null, "topic0", "topic1"] | 
					
						
							|  |  |  | 				for _, rawTopic := range topic { | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 					if rawTopic == nil { | 
					
						
							| 
									
										
										
										
											2017-09-27 13:14:52 +03:00
										 |  |  | 						// null component, match all | 
					
						
							|  |  |  | 						args.Topics[i] = nil | 
					
						
							|  |  |  | 						break | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 					if topic, ok := rawTopic.(string); ok { | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 						parsed, err := decodeTopic(topic) | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 						if err != nil { | 
					
						
							|  |  |  | 							return err | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 						} | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 						args.Topics[i] = append(args.Topics[i], parsed) | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						return fmt.Errorf("invalid topic(s)") | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 			default: | 
					
						
							| 
									
										
										
										
											2016-05-17 16:05:12 +02:00
										 |  |  | 				return fmt.Errorf("invalid topic(s)") | 
					
						
							| 
									
										
										
										
											2015-10-15 16:07:19 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2016-11-28 02:21:46 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | func decodeAddress(s string) (common.Address, error) { | 
					
						
							|  |  |  | 	b, err := hexutil.Decode(s) | 
					
						
							|  |  |  | 	if err == nil && len(b) != common.AddressLength { | 
					
						
							|  |  |  | 		err = fmt.Errorf("hex has invalid length %d after decoding", len(b)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return common.BytesToAddress(b), err | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func decodeTopic(s string) (common.Hash, error) { | 
					
						
							|  |  |  | 	b, err := hexutil.Decode(s) | 
					
						
							|  |  |  | 	if err == nil && len(b) != common.HashLength { | 
					
						
							|  |  |  | 		err = fmt.Errorf("hex has invalid length %d after decoding", len(b)) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return common.BytesToHash(b), err | 
					
						
							|  |  |  | } |