eth/filters, ethereum: EIP-234 add blockHash param for eth_getLogs

This commit is contained in:
Domino Valdano 2018-05-12 02:20:46 -07:00 committed by Daniel Liu
parent 8edb76f08d
commit 69b4383528
5 changed files with 97 additions and 22 deletions

View file

@ -527,6 +527,10 @@ func (fb *filterBackend) HeaderByNumber(ctx context.Context, block rpc.BlockNumb
return fb.bc.GetHeaderByNumber(uint64(block.Int64())), nil
}
func (fb *filterBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) {
return fb.bc.GetHeaderByHash(blockHash), nil
}
func (fb *filterBackend) GetReceipts(ctx context.Context, hash common.Hash) (types.Receipts, error) {
return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
}

View file

@ -35,6 +35,13 @@ import (
"github.com/XinFinOrg/XDPoSChain/rpc"
)
var (
errExceedMaxTopics = errors.New("exceed max topics")
)
// The maximum number of topic criteria allowed, vm.LOG4 - vm.LOG0
const maxTopics = 4
var (
deadline = 5 * time.Minute // consider a filter inactive if it has not been polled for within deadline
)
@ -99,7 +106,7 @@ func (api *PublicFilterAPI) timeoutLoop() {
// NewPendingTransactionFilter creates a filter that fetches pending transaction hashes
// as transactions enter the pending state.
//
// It is part of the filter package because this filter can be used throug the
// It is part of the filter package because this filter can be used through the
// `eth_getFilterChanges` polling method that is also used for log filters.
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter
@ -269,14 +276,8 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
}
// FilterCriteria represents a request to create a new filter.
//
// TODO(karalabe): Kill this in favor of ethereum.FilterQuery.
type FilterCriteria struct {
FromBlock *big.Int
ToBlock *big.Int
Addresses []common.Address
Topics [][]common.Hash
}
// Same as ethereum.FilterQuery but with UnmarshalJSON() method.
type FilterCriteria ethereum.FilterQuery
// 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
@ -327,15 +328,42 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
//
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) {
// Convert the RPC block numbers into internal representations
if crit.FromBlock == nil {
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
if len(crit.Topics) > maxTopics {
return nil, errExceedMaxTopics
}
if crit.ToBlock == nil {
crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
var (
fromBlock int64
toBlock int64
)
if crit.BlockHash != nil {
// look up block number from block hash
header, err := api.backend.HeaderByHash(ctx, *crit.BlockHash)
if err != nil {
return nil, err
}
if header == nil {
return nil, errors.New("unknown block")
}
fromBlock = int64(header.Number.Int64())
toBlock = fromBlock
} else {
// Convert the RPC block numbers into internal representations
if crit.FromBlock == nil {
fromBlock = int64(rpc.LatestBlockNumber)
} else {
fromBlock = crit.FromBlock.Int64()
}
if crit.ToBlock == nil {
toBlock = int64(rpc.LatestBlockNumber)
} else {
toBlock = crit.ToBlock.Int64()
}
}
// Create and run the filter to get all the logs
filter := New(api.backend, crit.FromBlock.Int64(), crit.ToBlock.Int64(), crit.Addresses, crit.Topics)
filter := New(api.backend, fromBlock, toBlock, crit.Addresses, crit.Topics)
logs, err := filter.Logs(ctx)
if err != nil {
@ -451,7 +479,8 @@ func returnLogs(logs []*types.Log) []*types.Log {
// UnmarshalJSON sets *args fields with given data.
func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
type input struct {
From *rpc.BlockNumber `json:"fromBlock"`
BlockHash *common.Hash `json:"blockHash"`
FromBlock *rpc.BlockNumber `json:"fromBlock"`
ToBlock *rpc.BlockNumber `json:"toBlock"`
Addresses interface{} `json:"address"`
Topics []interface{} `json:"topics"`
@ -462,12 +491,20 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
return err
}
if raw.From != nil {
args.FromBlock = big.NewInt(raw.From.Int64())
}
if raw.BlockHash != nil {
if raw.FromBlock != nil || raw.ToBlock != nil {
// BlockHash is mutually exclusive with FromBlock/ToBlock criteria
return fmt.Errorf("cannot specify both BlockHash and FromBlock/ToBlock, choose one or the other")
}
args.BlockHash = raw.BlockHash
} else {
if raw.FromBlock != nil {
args.FromBlock = big.NewInt(raw.FromBlock.Int64())
}
if raw.ToBlock != nil {
args.ToBlock = big.NewInt(raw.ToBlock.Int64())
if raw.ToBlock != nil {
args.ToBlock = big.NewInt(raw.ToBlock.Int64())
}
}
args.Addresses = []common.Address{}

View file

@ -33,6 +33,7 @@ type Backend interface {
ChainDb() ethdb.Database
EventMux() *event.TypeMux
HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error)
HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error)
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
GetLogs(ctx context.Context, blockHash common.Hash) ([][]*types.Log, error)

View file

@ -19,7 +19,6 @@ package filters
import (
"context"
"fmt"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big"
"math/rand"
"reflect"
@ -31,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/bloombits"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/event"
@ -69,6 +69,11 @@ func (b *testBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumbe
return core.GetHeader(b.db, hash, num), nil
}
func (b *testBackend) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) {
num := core.GetBlockNumber(b.db, blockHash)
return core.GetHeader(b.db, blockHash, num), nil
}
func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
number := core.GetBlockNumber(b.db, blockHash)
return core.GetBlockReceipts(b.db, blockHash, number), nil
@ -335,6 +340,33 @@ func TestInvalidLogFilterCreation(t *testing.T) {
}
}
func TestInvalidGetLogsRequest(t *testing.T) {
var (
mux = new(event.TypeMux)
db = rawdb.NewMemoryDatabase()
txFeed = new(event.Feed)
rmLogsFeed = new(event.Feed)
logsFeed = new(event.Feed)
chainFeed = new(event.Feed)
backend = &testBackend{mux, db, 0, txFeed, rmLogsFeed, logsFeed, chainFeed}
api = NewPublicFilterAPI(backend, false)
blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
)
// Reason: Cannot specify both BlockHash and FromBlock/ToBlock)
testCases := []FilterCriteria{
0: {BlockHash: &blockHash, FromBlock: big.NewInt(100)},
1: {BlockHash: &blockHash, ToBlock: big.NewInt(500)},
2: {BlockHash: &blockHash, FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
}
for i, test := range testCases {
if _, err := api.GetLogs(context.Background(), test); err == nil {
t.Errorf("Expected Logs for case #%d to fail", i)
}
}
}
// TestLogFilter tests whether log filters match the correct logs that are posted to the event feed.
func TestLogFilter(t *testing.T) {
t.Parallel()

View file

@ -132,6 +132,7 @@ type ContractCaller interface {
// FilterQuery contains options for contract log filtering.
type FilterQuery struct {
BlockHash *common.Hash // used by eth_getLogs, return logs only from block with this hash
FromBlock *big.Int // beginning of the queried range, nil means genesis block
ToBlock *big.Int // end of the range, nil means latest block
Addresses []common.Address // restricts matches to events created by specific contracts