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 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) { 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 return core.GetBlockReceipts(fb.db, hash, core.GetBlockNumber(fb.db, hash)), nil
} }

View file

@ -35,6 +35,13 @@ import (
"github.com/XinFinOrg/XDPoSChain/rpc" "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 ( var (
deadline = 5 * time.Minute // consider a filter inactive if it has not been polled for within deadline 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 // NewPendingTransactionFilter creates a filter that fetches pending transaction hashes
// as transactions enter the pending state. // 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. // `eth_getFilterChanges` polling method that is also used for log filters.
// //
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newpendingtransactionfilter // 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. // FilterCriteria represents a request to create a new filter.
// // Same as ethereum.FilterQuery but with UnmarshalJSON() method.
// TODO(karalabe): Kill this in favor of ethereum.FilterQuery. type FilterCriteria ethereum.FilterQuery
type FilterCriteria struct {
FromBlock *big.Int
ToBlock *big.Int
Addresses []common.Address
Topics [][]common.Hash
}
// NewFilter creates a new filter and returns the filter id. It can be // 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 // 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 // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) { func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) {
// Convert the RPC block numbers into internal representations if len(crit.Topics) > maxTopics {
if crit.FromBlock == nil { return nil, errExceedMaxTopics
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
} }
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 // 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) logs, err := filter.Logs(ctx)
if err != nil { if err != nil {
@ -451,7 +479,8 @@ func returnLogs(logs []*types.Log) []*types.Log {
// UnmarshalJSON sets *args fields with given data. // UnmarshalJSON sets *args fields with given data.
func (args *FilterCriteria) UnmarshalJSON(data []byte) error { func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
type input struct { type input struct {
From *rpc.BlockNumber `json:"fromBlock"` BlockHash *common.Hash `json:"blockHash"`
FromBlock *rpc.BlockNumber `json:"fromBlock"`
ToBlock *rpc.BlockNumber `json:"toBlock"` ToBlock *rpc.BlockNumber `json:"toBlock"`
Addresses interface{} `json:"address"` Addresses interface{} `json:"address"`
Topics []interface{} `json:"topics"` Topics []interface{} `json:"topics"`
@ -462,12 +491,20 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
return err return err
} }
if raw.From != nil { if raw.BlockHash != nil {
args.FromBlock = big.NewInt(raw.From.Int64()) 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 { if raw.ToBlock != nil {
args.ToBlock = big.NewInt(raw.ToBlock.Int64()) args.ToBlock = big.NewInt(raw.ToBlock.Int64())
}
} }
args.Addresses = []common.Address{} args.Addresses = []common.Address{}

View file

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

View file

@ -19,7 +19,6 @@ package filters
import ( import (
"context" "context"
"fmt" "fmt"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"math/big" "math/big"
"math/rand" "math/rand"
"reflect" "reflect"
@ -31,6 +30,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/consensus/ethash" "github.com/XinFinOrg/XDPoSChain/consensus/ethash"
"github.com/XinFinOrg/XDPoSChain/core" "github.com/XinFinOrg/XDPoSChain/core"
"github.com/XinFinOrg/XDPoSChain/core/bloombits" "github.com/XinFinOrg/XDPoSChain/core/bloombits"
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/types" "github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/ethdb" "github.com/XinFinOrg/XDPoSChain/ethdb"
"github.com/XinFinOrg/XDPoSChain/event" "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 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) { func (b *testBackend) GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error) {
number := core.GetBlockNumber(b.db, blockHash) number := core.GetBlockNumber(b.db, blockHash)
return core.GetBlockReceipts(b.db, blockHash, number), nil 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. // TestLogFilter tests whether log filters match the correct logs that are posted to the event feed.
func TestLogFilter(t *testing.T) { func TestLogFilter(t *testing.T) {
t.Parallel() t.Parallel()

View file

@ -132,6 +132,7 @@ type ContractCaller interface {
// FilterQuery contains options for contract log filtering. // FilterQuery contains options for contract log filtering.
type FilterQuery struct { 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 FromBlock *big.Int // beginning of the queried range, nil means genesis block
ToBlock *big.Int // end of the range, nil means latest block ToBlock *big.Int // end of the range, nil means latest block
Addresses []common.Address // restricts matches to events created by specific contracts Addresses []common.Address // restricts matches to events created by specific contracts