Filtering for eth_getLogs (#120)
* Filtered logs based param criteria * addded PublicFilterAPI * added filter logs func to filter based on params * added Unmarshal func from go-ethereum * Linted * made requested changes
This commit is contained in:
parent
eab81bc578
commit
a61f3b892d
2
go.mod
2
go.mod
@ -50,9 +50,9 @@ require (
|
||||
github.com/tyler-smith/go-bip39 v1.0.0 // indirect
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/appengine v1.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 // indirect
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||
gopkg.in/yaml.v2 v2.2.2
|
||||
|
@ -30,5 +30,11 @@ func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []r
|
||||
Service: NewPersonalEthAPI(cliCtx, nonceLock),
|
||||
Public: false,
|
||||
},
|
||||
{
|
||||
Namespace: "eth",
|
||||
Version: "1.0",
|
||||
Service: NewPublicFilterAPI(cliCtx),
|
||||
Public: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
80
rpc/filter_api.go
Normal file
80
rpc/filter_api.go
Normal file
@ -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
|
||||
}
|
84
rpc/filters.go
Normal file
84
rpc/filters.go
Normal file
@ -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 {
|
||||
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
|
||||
}
|
@ -24,6 +24,7 @@ const (
|
||||
QueryHashToHeight = "hashToHeight"
|
||||
QueryTxLogs = "txLogs"
|
||||
QueryLogsBloom = "logsBloom"
|
||||
QueryLogs = "logs"
|
||||
)
|
||||
|
||||
// NewQuerier is the module level router for state queries
|
||||
@ -48,6 +49,8 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
||||
return queryTxLogs(ctx, path, keeper)
|
||||
case QueryLogsBloom:
|
||||
return queryBlockLogsBloom(ctx, path, keeper)
|
||||
case QueryLogs:
|
||||
return queryLogs(ctx, path, keeper)
|
||||
default:
|
||||
return nil, sdk.ErrUnknownRequest("unknown query endpoint")
|
||||
}
|
||||
@ -167,3 +170,13 @@ func queryTxLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Err
|
||||
|
||||
return res, nil
|
||||
}
|
||||
|
||||
func queryLogs(ctx sdk.Context, path []string, keeper Keeper) ([]byte, sdk.Error) {
|
||||
logs := keeper.Logs(ctx)
|
||||
|
||||
l, err := codec.MarshalJSONIndent(keeper.cdc, logs)
|
||||
if err != nil {
|
||||
panic("could not marshal result to JSON: " + err.Error())
|
||||
}
|
||||
return l, nil
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user