This repository has been archived by the owner on Nov 30, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 161
Filtering for eth_getLogs #120
Merged
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package rpc | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/cosmos/cosmos-sdk/client/context" | ||
"github.com/cosmos/ethermint/x/evm/types" | ||
|
||
"math/big" | ||
|
||
ethtypes "github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/eth/filters" | ||
"github.com/ethereum/go-ethereum/rpc" | ||
) | ||
|
||
// PublicFilterAPI is the eth_ prefixed set of APIs in the Web3 JSON-RPC spec. | ||
type PublicFilterAPI struct { | ||
cliCtx context.CLIContext | ||
} | ||
|
||
// NewPublicEthAPI creates an instance of the public ETH Web3 API. | ||
func NewPublicFilterAPI(cliCtx context.CLIContext) *PublicFilterAPI { | ||
return &PublicFilterAPI{ | ||
cliCtx: cliCtx, | ||
} | ||
} | ||
|
||
// GetLogs returns logs matching the given argument that are stored within the state. | ||
// | ||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs | ||
func (e *PublicFilterAPI) GetLogs(criteria filters.FilterCriteria) ([]*ethtypes.Log, error) { | ||
var filter *Filter | ||
if criteria.BlockHash != nil { | ||
/* | ||
Still need to add blockhash in prepare function for log entry | ||
*/ | ||
filter = NewBlockFilter(*criteria.BlockHash, criteria.Addresses, criteria.Topics) | ||
results := e.getLogs() | ||
logs := filterLogs(results, nil, nil, filter.addresses, filter.topics) | ||
return logs, nil | ||
} else { | ||
// Convert the RPC block numbers into internal representations | ||
begin := rpc.LatestBlockNumber.Int64() | ||
if criteria.FromBlock != nil { | ||
begin = criteria.FromBlock.Int64() | ||
} | ||
from := big.NewInt(begin) | ||
end := rpc.LatestBlockNumber.Int64() | ||
if criteria.ToBlock != nil { | ||
end = criteria.ToBlock.Int64() | ||
} | ||
to := big.NewInt(end) | ||
results := e.getLogs() | ||
logs := filterLogs(results, from, to, criteria.Addresses, criteria.Topics) | ||
|
||
return returnLogs(logs), nil | ||
} | ||
} | ||
|
||
func (e *PublicFilterAPI) getLogs() (results []*ethtypes.Log) { | ||
l, _, err := e.cliCtx.QueryWithData(fmt.Sprintf("custom/%s/logs", types.ModuleName), nil) | ||
if err != nil { | ||
fmt.Printf("error from querier %e ", err) | ||
} | ||
|
||
if err := json.Unmarshal(l, &results); err != nil { | ||
panic(err) | ||
} | ||
return results | ||
} | ||
|
||
// 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. | ||
func returnLogs(logs []*ethtypes.Log) []*ethtypes.Log { | ||
if logs == nil { | ||
return []*ethtypes.Log{} | ||
} | ||
return logs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package rpc | ||
|
||
import ( | ||
"math/big" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
ethtypes "github.com/ethereum/go-ethereum/core/types" | ||
) | ||
|
||
/* | ||
- Filter functions derived from go-ethereum | ||
Used to set the criteria passed in from RPC params | ||
*/ | ||
|
||
// Filter can be used to retrieve and filter logs. | ||
type Filter struct { | ||
dutterbutter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
addresses []common.Address | ||
topics [][]common.Hash | ||
|
||
block common.Hash // Block hash if filtering a single block | ||
} | ||
|
||
// NewBlockFilter creates a new filter which directly inspects the contents of | ||
// a block to figure out whether it is interesting or not. | ||
func NewBlockFilter(block common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter { | ||
// Create a generic filter and convert it into a block filter | ||
filter := newFilter(addresses, topics) | ||
filter.block = block | ||
return filter | ||
} | ||
|
||
// newFilter creates a generic filter that can either filter based on a block hash, | ||
// or based on range queries. The search criteria needs to be explicitly set. | ||
func newFilter(addresses []common.Address, topics [][]common.Hash) *Filter { | ||
return &Filter{ | ||
addresses: addresses, | ||
topics: topics, | ||
} | ||
} | ||
|
||
func includes(addresses []common.Address, a common.Address) bool { | ||
for _, addr := range addresses { | ||
if addr == a { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} | ||
|
||
// filterLogs creates a slice of logs matching the given criteria. | ||
func filterLogs(logs []*ethtypes.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*ethtypes.Log { | ||
var ret []*ethtypes.Log | ||
Logs: | ||
for _, log := range logs { | ||
if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber { | ||
continue | ||
} | ||
if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber { | ||
continue | ||
} | ||
if len(addresses) > 0 && !includes(addresses, log.Address) { | ||
continue | ||
} | ||
// If the to filtered topics is greater than the amount of topics in logs, skip. | ||
if len(topics) > len(log.Topics) { | ||
continue Logs | ||
} | ||
for i, sub := range topics { | ||
match := len(sub) == 0 // empty rule set == wildcard | ||
for _, topic := range sub { | ||
if log.Topics[i] == topic { | ||
match = true | ||
break | ||
} | ||
} | ||
if !match { | ||
continue Logs | ||
} | ||
} | ||
ret = append(ret, log) | ||
} | ||
return ret | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it not be beneficial to include the range filter on the type you created to abstract this logic so you aren't doing it duplicate times?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can you elaborate on what you mean from the above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just replicating the e.getLogs and filterLogs calls, can't the filter just be generated in this if/ else then call these equivalently outside of it for consistency?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
still need some clarity here....
e.getLogs
is the actual call to retrieve the logs whereasfilterLogs
does the filtering. We want to keep this functionality separate when we introduce bloom filters so that we only callfilterLogs
on the logs that may meet the filter criteria which will be indicated by the bloom filter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be more specific, I just mean having the functionality to create a range filter like you do with
NewBlockFilter()
then get logs and filter them below this conditional (let me know what you think of thisThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The only problem I have is that you are handling block filters and range filters asymmetrically, which is somewhat hard to follow. Also small thing with this is that it adds a linting issue with returning both in the coniditonal (that doesn't matter as much)