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/tyler-smith/go-bip39 v1.0.0 // indirect
|
||||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
|
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208 // indirect
|
||||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4
|
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/sys v0.0.0-20190626221950-04f50cda93cb // indirect
|
||||||
golang.org/x/text v0.3.2 // 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
|
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 // indirect
|
||||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce // indirect
|
||||||
gopkg.in/yaml.v2 v2.2.2
|
gopkg.in/yaml.v2 v2.2.2
|
||||||
|
@ -30,5 +30,11 @@ func GetRPCAPIs(cliCtx context.CLIContext, key emintcrypto.PrivKeySecp256k1) []r
|
|||||||
Service: NewPersonalEthAPI(cliCtx, nonceLock),
|
Service: NewPersonalEthAPI(cliCtx, nonceLock),
|
||||||
Public: false,
|
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"
|
QueryHashToHeight = "hashToHeight"
|
||||||
QueryTxLogs = "txLogs"
|
QueryTxLogs = "txLogs"
|
||||||
QueryLogsBloom = "logsBloom"
|
QueryLogsBloom = "logsBloom"
|
||||||
|
QueryLogs = "logs"
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewQuerier is the module level router for state queries
|
// NewQuerier is the module level router for state queries
|
||||||
@ -48,6 +49,8 @@ func NewQuerier(keeper Keeper) sdk.Querier {
|
|||||||
return queryTxLogs(ctx, path, keeper)
|
return queryTxLogs(ctx, path, keeper)
|
||||||
case QueryLogsBloom:
|
case QueryLogsBloom:
|
||||||
return queryBlockLogsBloom(ctx, path, keeper)
|
return queryBlockLogsBloom(ctx, path, keeper)
|
||||||
|
case QueryLogs:
|
||||||
|
return queryLogs(ctx, path, keeper)
|
||||||
default:
|
default:
|
||||||
return nil, sdk.ErrUnknownRequest("unknown query endpoint")
|
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
|
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