Skip to content
This repository has been archived by the owner on Nov 30, 2021. It is now read-only.

eth_getFilterLogs, eth_getLogs implementation #248

Merged
merged 88 commits into from
Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from 83 commits
Commits
Show all changes
88 commits
Select commit Hold shift + click to select a range
832759e
evm: move Keeper and Querier to /keeper package
fedekunze Mar 6, 2020
e5945f8
keeper: update keeper_test.go
fedekunze Mar 6, 2020
2d1a7b4
fix format
fedekunze Mar 6, 2020
23a7822
evm: use aliased types
fedekunze Mar 6, 2020
2e3081c
bump SDK version to v0.38.1
fedekunze Mar 6, 2020
f01326c
app: updates from new version
fedekunze Mar 6, 2020
213e278
errors: switch sdk.Error -> error
fedekunze Mar 6, 2020
af010c6
errors: switch sdk.Error -> error. Continuation
fedekunze Mar 6, 2020
28e94fb
more fixes
fedekunze Mar 6, 2020
865d962
update app/
fedekunze Mar 9, 2020
ad347a0
update keys and client pkgs
fedekunze Mar 9, 2020
1add197
merge master
fedekunze Mar 9, 2020
359de7a
build
fedekunze Mar 10, 2020
e41a25e
fix tests
fedekunze Mar 10, 2020
230ddf8
lint
fedekunze Mar 10, 2020
569a511
minor changes
fedekunze Mar 11, 2020
2c0dc2a
changelog
fedekunze Mar 11, 2020
6122b3c
address @austinbell comments
fedekunze Mar 11, 2020
fae679b
Fix keyring usage in rpc API and CLI
austinabell Mar 11, 2020
3988040
fix keyring
fedekunze Mar 12, 2020
ff91e3c
break line
fedekunze Mar 12, 2020
729ebf2
Misc cleanup (#188)
fedekunze Mar 12, 2020
b70f6cb
evm: update statedb to create ethermint Account instead of BaseAccount
fedekunze Mar 12, 2020
9eaf77f
fix importer test
fedekunze Mar 12, 2020
9db72db
address @austinabell comments
fedekunze Mar 13, 2020
2abf58b
update README
fedekunze Mar 13, 2020
ace3028
changelog
fedekunze Mar 13, 2020
6036b76
evm: update codec
fedekunze Mar 13, 2020
b37053e
Merge branch 'development' into fedekunze/177-bump-sdk-version
fedekunze Mar 13, 2020
84935a4
merge development
fedekunze Mar 16, 2020
89bfa6f
merge development
fedekunze Mar 17, 2020
cc59134
fix event sender
fedekunze Mar 17, 2020
d0aa494
store logs in keeper after transition (#210)
noot Mar 18, 2020
f92b815
fix encoding bug
fedekunze Mar 18, 2020
c955ed2
minor fix
fedekunze Mar 19, 2020
c05a72c
rpc: error handling
fedekunze Mar 19, 2020
aca648b
rpc: simulate only returns gasConsumed
fedekunze Mar 19, 2020
98c92f7
rpc: error ineffassign
fedekunze Mar 19, 2020
66466e2
add tx hash to ResultData, begin querier test
noot Mar 19, 2020
479eb86
write querier test for txlogs
noot Mar 23, 2020
cbc3f4d
add GetTxLogs to eth_api
noot Mar 23, 2020
40103d7
cleanup
noot Mar 23, 2020
727bc4e
update eth_api; move test to handler_test
noot Mar 24, 2020
0448d6a
initial filter struct
noot Mar 24, 2020
2c17b1a
finish adding eth_ methods
noot Mar 25, 2020
cce1f75
update GetLogs
noot Mar 25, 2020
d350583
lint
noot Mar 25, 2020
4e18218
use hexutil; FilterCriteria
noot Mar 25, 2020
fc9b606
add EmintBackend, migrate eth_ methods to EmintBackend
noot Mar 25, 2020
b94dd95
lint
noot Mar 25, 2020
97fa4d8
use rpc.ID for filter api
noot Mar 25, 2020
d39c7b0
address comments
noot Mar 26, 2020
54cacb7
update comment
noot Mar 26, 2020
8435c78
set gasLimit default
noot Mar 26, 2020
8616813
add pollForBlocks function
noot Mar 27, 2020
24d449f
implement getFilterChanges for BlockFilter
noot Mar 27, 2020
1d5a7ae
lint
noot Mar 27, 2020
3041a13
beginning getFilterLogs
noot Apr 1, 2020
10d9e69
merge with development
noot Apr 2, 2020
a72d71c
merge
noot Apr 2, 2020
e4f40c2
merge with development
noot Apr 2, 2020
c46cdb7
Merge branch 'noot/filter' of github.com:ChainSafe/ethermint into noo…
noot Apr 2, 2020
dca52cb
Merge branch 'noot/block-filter' of github.com:ChainSafe/ethermint in…
noot Apr 2, 2020
262db9a
merge with development
noot Apr 2, 2020
53050f1
working on getFilterLogs
noot Apr 3, 2020
1284f35
Merge branch 'development' of github.com:ChainSafe/ethermint into noo…
noot Apr 7, 2020
aaef2e6
implement checkMatches, initial implementation of getFilterLogs
noot Apr 7, 2020
49cd628
testing filter logs
noot Apr 7, 2020
c08afec
testing
noot Apr 8, 2020
1813ef6
add eth_sendTransaction test
noot Apr 9, 2020
cae18c4
fix GetBlockByNumber with fulltxs=true
noot Apr 10, 2020
68879c0
working on getTransactionReceipt test
noot Apr 10, 2020
bfc656a
adding logs
noot Apr 10, 2020
b76cb0e
Merge branch 'development' of github.com:ChainSafe/ethermint into noo…
noot Apr 10, 2020
4142813
eth_getTxLogs working
noot Apr 10, 2020
22d5cf5
update to development
noot Apr 11, 2020
4bccf3b
getFilterChanges with no topics working
noot Apr 11, 2020
6ead1d1
add filter test with topics
noot Apr 11, 2020
56acee8
update filter to use block txs
noot Apr 13, 2020
be837f3
tests working
noot Apr 13, 2020
d7c5a90
update eth_getLogs
noot Apr 13, 2020
a98858a
cleanup
noot Apr 13, 2020
b8c0761
add tests
noot Apr 13, 2020
d659960
cleanup
noot Apr 13, 2020
1a3716c
cleanup tests
noot Apr 13, 2020
7ca8640
address comments
noot Apr 13, 2020
af81334
fix deploy err
noot Apr 13, 2020
b46a8a5
update changelog
noot Apr 13, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 18 additions & 4 deletions rpc/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ type Backend interface {
// Used by block filter; also used for polling
BlockNumber() (hexutil.Uint64, error)
GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[string]interface{}, error)
GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error)
getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error)
getGasLimit() (int64, error)

Expand Down Expand Up @@ -62,6 +63,18 @@ func (e *EthermintBackend) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (
return e.getEthBlockByNumber(value, fullTx)
}

// GetBlockByHash returns the block identified by hash.
func (e *EthermintBackend) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryHashToHeight, hash.Hex()))
if err != nil {
return nil, err
}

var out types.QueryResBlockNumber
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
noot marked this conversation as resolved.
Show resolved Hide resolved
return e.getEthBlockByNumber(out.Number, fullTx)
}

func (e *EthermintBackend) getEthBlockByNumber(height int64, fullTx bool) (map[string]interface{}, error) {
// Remove this check when 0 query is fixed ref: (https://github.com/tendermint/tendermint/issues/4014)
var blkNumPtr *int64
Expand All @@ -82,7 +95,7 @@ func (e *EthermintBackend) getEthBlockByNumber(height int64, fullTx bool) (map[s

var (
gasUsed *big.Int
transactions []interface{}
transactions []common.Hash
)

if fullTx {
Expand All @@ -96,7 +109,7 @@ func (e *EthermintBackend) getEthBlockByNumber(height int64, fullTx bool) (map[s
} else {
// TODO: Gas used not saved and cannot be calculated by hashes
// Return slice of transaction hashes
transactions = make([]interface{}, len(block.Block.Txs))
transactions = make([]common.Hash, len(block.Block.Txs))
for i, tx := range block.Block.Txs {
transactions[i] = common.BytesToHash(tx.Hash())
}
Expand Down Expand Up @@ -142,13 +155,14 @@ func (e *EthermintBackend) getGasLimit() (int64, error) {
func (e *EthermintBackend) GetTxLogs(txHash common.Hash) ([]*ethtypes.Log, error) {
// do we need to use the block height somewhere?
ctx := e.cliCtx

res, _, err := ctx.QueryWithData(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryTxLogs, txHash.Hex()), nil)
if err != nil {
return nil, err
}

var out types.QueryETHLogs
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
out := new(types.QueryETHLogs)
e.cliCtx.Codec.MustUnmarshalJSON(res, out)
noot marked this conversation as resolved.
Show resolved Hide resolved
return out.Logs, nil
}

Expand Down
31 changes: 13 additions & 18 deletions rpc/eth_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -291,16 +291,14 @@ func (e *PublicEthAPI) SendTransaction(args params.SendTxArgs) (common.Hash, err
}

// Broadcast transaction
res, err := e.cliCtx.BroadcastTx(txBytes)
_, err = e.cliCtx.BroadcastTx(txBytes)
// If error is encountered on the node, the broadcast will not return an error
// TODO: Remove res log
fmt.Println(res.RawLog)
if err != nil {
return common.Hash{}, err
}

// Return transaction hash
return common.HexToHash(res.TxHash), nil
return tx.Hash(), nil
}

// SendRawTransaction send a raw Ethereum transaction.
Expand Down Expand Up @@ -471,14 +469,7 @@ func (e *PublicEthAPI) EstimateGas(args CallArgs) (hexutil.Uint64, error) {

// GetBlockByHash returns the block identified by hash.
func (e *PublicEthAPI) GetBlockByHash(hash common.Hash, fullTx bool) (map[string]interface{}, error) {
res, _, err := e.cliCtx.Query(fmt.Sprintf("custom/%s/%s/%s", types.ModuleName, evm.QueryHashToHeight, hash.Hex()))
if err != nil {
return nil, err
}

var out types.QueryResBlockNumber
e.cliCtx.Codec.MustUnmarshalJSON(res, &out)
return e.backend.getEthBlockByNumber(out.Number, fullTx)
return e.backend.GetBlockByHash(hash, fullTx)
}

// GetBlockByNumber returns the block identified by number.
Expand All @@ -488,7 +479,7 @@ func (e *PublicEthAPI) GetBlockByNumber(blockNum BlockNumber, fullTx bool) (map[

func formatBlock(
header tmtypes.Header, size int, gasLimit int64,
gasUsed *big.Int, transactions []interface{}, bloom ethtypes.Bloom,
gasUsed *big.Int, transactions interface{}, bloom ethtypes.Bloom,
) map[string]interface{} {
return map[string]interface{}{
"number": hexutil.Uint64(header.Height),
Expand All @@ -507,13 +498,13 @@ func formatBlock(
"gasLimit": hexutil.Uint64(gasLimit), // Static gas limit
"gasUsed": (*hexutil.Big)(gasUsed),
"timestamp": hexutil.Uint64(header.Time.Unix()),
"transactions": transactions,
"transactions": transactions.([]common.Hash),
"uncles": nil,
}
}

func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]interface{}, *big.Int, error) {
transactions := make([]interface{}, len(txs))
func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, blockHash common.Hash, height uint64) ([]common.Hash, *big.Int, error) {
transactions := make([]common.Hash, len(txs))
gasUsed := big.NewInt(0)

for i, tx := range txs {
Expand All @@ -523,7 +514,11 @@ func convertTransactionsToRPC(cliCtx context.CLIContext, txs []tmtypes.Tx, block
}
// TODO: Remove gas usage calculation if saving gasUsed per block
gasUsed.Add(gasUsed, ethTx.Fee())
transactions[i], err = newRPCTransaction(*ethTx, blockHash, &height, uint64(i))
tx, err := newRPCTransaction(*ethTx, blockHash, &height, uint64(i))
if err != nil {
return nil, nil, err
}
transactions[i] = tx.Hash
if err != nil {
return nil, nil, err
}
Expand Down Expand Up @@ -712,7 +707,7 @@ func (e *PublicEthAPI) GetTransactionReceipt(hash common.Hash) (map[string]inter
"to": ethTx.To(),
"gasUsed": hexutil.Uint64(tx.TxResult.GasUsed),
"cumulativeGasUsed": nil, // ignore until needed
"contractAddress": nil,
"contractAddress": nil, // TODO??
"logs": logs.Logs,
"logsBloom": data.Bloom,
"status": status,
Expand Down
56 changes: 6 additions & 50 deletions rpc/filter_api.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
package rpc

import (
"encoding/json"
"fmt"
"math/big"

"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/ethermint/x/evm/types"

ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/filters"
Expand Down Expand Up @@ -62,62 +59,21 @@ func (e *PublicFilterAPI) UninstallFilter(id rpc.ID) bool {
// If the filter is a block filter, it returns an array of block hashes.
// If the filter is a pending transaction filter, it returns an array of transaction hashes.
func (e *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
if e.filters[id] == nil {
return nil, fmt.Errorf("invalid filter ID")
noot marked this conversation as resolved.
Show resolved Hide resolved
}
return e.filters[id].getFilterChanges()
}

// GetFilterLogs returns an array of all logs matching filter with given id.
func (e *PublicFilterAPI) GetFilterLogs(id rpc.ID) []*ethtypes.Log {
func (e *PublicFilterAPI) GetFilterLogs(id rpc.ID) ([]*ethtypes.Log, error) {
return e.filters[id].getFilterLogs()
}

// 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 = NewFilterWithBlockHash(e.backend, &criteria)
results := e.getLogs()
logs := filterLogs(results, nil, nil, filter.addresses, filter.topics)
return logs, nil
}
// 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
filter := NewFilter(e.backend, &criteria)
return filter.getFilterLogs()
}
115 changes: 104 additions & 11 deletions rpc/filters.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/log"
)

/*
Expand Down Expand Up @@ -37,7 +38,7 @@ type Filter struct {

// NewFilter returns a new Filter
func NewFilter(backend Backend, criteria *filters.FilterCriteria) *Filter {
return &Filter{
filter := &Filter{
backend: backend,
fromBlock: criteria.FromBlock,
toBlock: criteria.ToBlock,
Expand All @@ -46,6 +47,8 @@ func NewFilter(backend Backend, criteria *filters.FilterCriteria) *Filter {
typ: logFilter,
stopped: false,
}

return filter
}

// NewFilterWithBlockHash returns a new Filter with a blockHash.
Expand Down Expand Up @@ -139,28 +142,108 @@ func (f *Filter) getFilterChanges() (interface{}, error) {
case pendingTxFilter:
// TODO
case logFilter:
// TODO
return f.getFilterLogs()
}

return nil, nil
return nil, errors.New("unsupported filter")
}

func (f *Filter) getFilterLogs() []*ethtypes.Log {
// TODO
return nil
func (f *Filter) getFilterLogs() ([]*ethtypes.Log, error) {
ret := []*ethtypes.Log{}

// filter specific block only
if f.blockHash != nil {
block, err := f.backend.GetBlockByHash(*f.blockHash, true)
if err != nil {
return nil, err
}

// if the logsBloom == 0, there are no logs in that block
if txs, ok := block["transactions"].([]common.Hash); !ok {
return ret, nil
} else if len(txs) != 0 {
return f.checkMatches(block)
}
}

// filter range of blocks
// TODO: check if toBlock or fromBlock is "latest", "pending", or "earliest"
num, err := f.backend.BlockNumber()
if err != nil {
return nil, err
}

// if f.fromBlock is set to 0, set it to the latest block number
if f.fromBlock == nil || f.fromBlock.Cmp(big.NewInt(0)) == 0 {
f.fromBlock = big.NewInt(int64(num))
}

// if f.toBlock is set to 0, set it to the latest block number
if f.toBlock == nil || f.toBlock.Cmp(big.NewInt(0)) == 0 {
f.toBlock = big.NewInt(int64(num))
}

log.Debug("[ethAPI] Retrieving filter logs", "fromBlock", f.fromBlock, "toBlock", f.toBlock,
"topics", f.topics, "addresses", f.addresses)

from := f.fromBlock.Int64()
to := f.toBlock.Int64()

for i := from; i <= to; i++ {
block, err := f.backend.GetBlockByNumber(NewBlockNumber(big.NewInt(i)), true)
if err != nil {
f.err = err
log.Debug("[ethAPI] Cannot get block", "block", block["number"], "error", err)
continue
noot marked this conversation as resolved.
Show resolved Hide resolved
}

log.Debug("[ethAPI] filtering", "block", block)

// TODO: block logsBloom is often set in the wrong block
// if the logsBloom == 0, there are no logs in that block

if txs, ok := block["transactions"].([]common.Hash); !ok {
noot marked this conversation as resolved.
Show resolved Hide resolved
continue
} else if len(txs) != 0 {
logs, err := f.checkMatches(block)
if err != nil {
f.err = err
continue
noot marked this conversation as resolved.
Show resolved Hide resolved
}

ret = append(ret, logs...)
}
}

return ret, nil
}

func includes(addresses []common.Address, a common.Address) bool {
for _, addr := range addresses {
if addr == a {
return true
func (f *Filter) checkMatches(block map[string]interface{}) ([]*ethtypes.Log, error) {
transactions, ok := block["transactions"].([]common.Hash)
if !ok {
return nil, errors.New("invalid block transactions")
}

unfiltered := []*ethtypes.Log{}

for _, tx := range transactions {
logs, err := f.backend.GetTxLogs(common.BytesToHash(tx[:]))
if err != nil {
return nil, err
}

unfiltered = append(unfiltered, logs...)
}

return false
return filterLogs(unfiltered, f.fromBlock, f.toBlock, f.addresses, f.topics), nil
}

// filterLogs creates a slice of logs matching the given criteria.
// [] -> anything
// [A] -> A in first position of log topics, anything after
// [null, B] -> anything in first position, B in second position
// [A, B] -> A in first position and B in second position
// [[A, B], [A, B]] -> A or B in first position, A or B in second position
func filterLogs(logs []*ethtypes.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*ethtypes.Log {
var ret []*ethtypes.Log
Logs:
Expand Down Expand Up @@ -194,3 +277,13 @@ Logs:
}
return ret
}

func includes(addresses []common.Address, a common.Address) bool {
for _, addr := range addresses {
if addr == a {
return true
}
}

return false
}
Loading