This commit is contained in:
Nick Johnson 2018-10-13 10:28:25 +00:00 committed by GitHub
commit 5cc42a3307
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 330 additions and 135 deletions

View file

@ -319,6 +319,21 @@ func (b *SimulatedBackend) SendTransaction(ctx context.Context, tx *types.Transa
return nil return nil
} }
// getBlockHash returns the block hash of the requested block.
// `hash` is used if supplied; if not the canonoical block at `number` is looked up.
// If neither is supplied, the latest canonical block hash is returned.
func (b *SimulatedBackend) getBlockHash(number *big.Int, hash *common.Hash) (common.Hash, error) {
if hash != nil {
return *hash, nil
} else if number != nil {
header := b.blockchain.GetHeaderByNumber(number.Uint64())
return header.Hash(), nil
} else {
header := b.blockchain.CurrentHeader()
return header.Hash(), nil
}
}
// FilterLogs executes a log filter operation, blocking during execution and // FilterLogs executes a log filter operation, blocking during execution and
// returning all the results in one batch. // returning all the results in one batch.
// //
@ -330,13 +345,13 @@ func (b *SimulatedBackend) FilterLogs(ctx context.Context, query ethereum.Filter
filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics) filter = filters.NewBlockFilter(&filterBackend{b.database, b.blockchain}, *query.BlockHash, query.Addresses, query.Topics)
} else { } else {
// Initialize unset filter boundaried to run from genesis to chain head // Initialize unset filter boundaried to run from genesis to chain head
from := int64(0) from, err := b.getBlockHash(query.FromBlock, query.FromBlockHash)
if query.FromBlock != nil { if err != nil {
from = query.FromBlock.Int64() return nil, err
} }
to := int64(-1) to, err := b.getBlockHash(query.ToBlock, query.ToBlockHash)
if query.ToBlock != nil { if err != nil {
to = query.ToBlock.Int64() return nil, err
} }
// Construct the range filter // Construct the range filter
filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics) filter = filters.NewRangeFilter(&filterBackend{b.database, b.blockchain}, from, to, query.Addresses, query.Topics)

View file

@ -320,6 +320,27 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
return logsSub.ID, nil return logsSub.ID, nil
} }
// getBlockHash returns the block hash of the requested block.
// `hash` is used if supplied; if not the canonoical block at `number` is looked up.
// If neither is supplied, the latest canonical block hash is returned.
func (api *PublicFilterAPI) getBlockHash(ctx context.Context, number *big.Int, hash *common.Hash) (common.Hash, error) {
if hash != nil {
return *hash, nil
} else if number != nil {
header, err := api.backend.HeaderByNumber(ctx, rpc.BlockNumber(number.Int64()))
if err != nil {
return common.Hash{}, err
}
return header.Hash(), nil
} else {
header, err := api.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if err != nil {
return common.Hash{}, err
}
return header.Hash(), nil
}
}
// GetLogs returns logs matching the given argument that are stored within the state. // GetLogs returns logs matching the given argument that are stored within the state.
// //
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs // https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
@ -329,14 +350,13 @@ func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([
// Block filter requested, construct a single-shot filter // Block filter requested, construct a single-shot filter
filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics) filter = NewBlockFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics)
} else { } else {
// Convert the RPC block numbers into internal representations begin, err := api.getBlockHash(ctx, crit.FromBlock, crit.FromBlockHash)
begin := rpc.LatestBlockNumber.Int64() if err != nil {
if crit.FromBlock != nil { return nil, err
begin = crit.FromBlock.Int64()
} }
end := rpc.LatestBlockNumber.Int64() end, err := api.getBlockHash(ctx, crit.ToBlock, crit.ToBlockHash)
if crit.ToBlock != nil { if err != nil {
end = crit.ToBlock.Int64() return nil, err
} }
// Construct the range filter // Construct the range filter
filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics) filter = NewRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics)
@ -384,14 +404,13 @@ func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*ty
// Block filter requested, construct a single-shot filter // Block filter requested, construct a single-shot filter
filter = NewBlockFilter(api.backend, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics) filter = NewBlockFilter(api.backend, *f.crit.BlockHash, f.crit.Addresses, f.crit.Topics)
} else { } else {
// Convert the RPC block numbers into internal representations begin, err := api.getBlockHash(ctx, f.crit.FromBlock, f.crit.FromBlockHash)
begin := rpc.LatestBlockNumber.Int64() if err != nil {
if f.crit.FromBlock != nil { return nil, err
begin = f.crit.FromBlock.Int64()
} }
end := rpc.LatestBlockNumber.Int64() end, err := api.getBlockHash(ctx, f.crit.ToBlock, f.crit.ToBlockHash)
if f.crit.ToBlock != nil { if err != nil {
end = f.crit.ToBlock.Int64() return nil, err
} }
// Construct the range filter // Construct the range filter
filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics) filter = NewRangeFilter(api.backend, begin, end, f.crit.Addresses, f.crit.Topics)
@ -460,8 +479,8 @@ func returnLogs(logs []*types.Log) []*types.Log {
func (args *FilterCriteria) UnmarshalJSON(data []byte) error { func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
type input struct { type input struct {
BlockHash *common.Hash `json:"blockHash"` BlockHash *common.Hash `json:"blockHash"`
FromBlock *rpc.BlockNumber `json:"fromBlock"` FromBlock *rpc.BlockNumberOrHash `json:"fromBlock"`
ToBlock *rpc.BlockNumber `json:"toBlock"` ToBlock *rpc.BlockNumberOrHash `json:"toBlock"`
Addresses interface{} `json:"address"` Addresses interface{} `json:"address"`
Topics []interface{} `json:"topics"` Topics []interface{} `json:"topics"`
} }
@ -479,11 +498,18 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
args.BlockHash = raw.BlockHash args.BlockHash = raw.BlockHash
} else { } else {
if raw.FromBlock != nil { if raw.FromBlock != nil {
args.FromBlock = big.NewInt(raw.FromBlock.Int64()) if raw.FromBlock.IsHash() {
args.FromBlockHash = raw.FromBlock.Hash()
} else {
args.FromBlock = big.NewInt(int64(raw.FromBlock.Number()))
}
} }
if raw.ToBlock != nil { if raw.ToBlock != nil {
args.ToBlock = big.NewInt(raw.ToBlock.Int64()) if raw.ToBlock.IsHash() {
args.ToBlockHash = raw.ToBlock.Hash()
} else {
args.ToBlock = big.NewInt(int64(raw.ToBlock.Number()))
}
} }
} }

View file

@ -29,6 +29,8 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
var ( var (
fromBlock rpc.BlockNumber = 0x123435 fromBlock rpc.BlockNumber = 0x123435
toBlock rpc.BlockNumber = 0xabcdef toBlock rpc.BlockNumber = 0xabcdef
fromBlockHash = common.HexToHash("ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1ca3")
toBlockHash = common.HexToHash("084a792d2f8b16a62b882fd56f7860c07bf5fa91dd8a2ae7e809e5180fef0b39")
address0 = common.HexToAddress("70c87d191324e6712a591f304b4eedef6ad9bb9d") address0 = common.HexToAddress("70c87d191324e6712a591f304b4eedef6ad9bb9d")
address1 = common.HexToAddress("9b2055d370f73ec7d8a03e965129118dc8f5bf83") address1 = common.HexToAddress("9b2055d370f73ec7d8a03e965129118dc8f5bf83")
topic0 = common.HexToHash("3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1ca") topic0 = common.HexToHash("3ac225168df54212a25c1c01fd35bebfea408fdac2e31ddd6f80a4bbf9a5f1ca")
@ -182,4 +184,17 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
if len(test7.Topics[2]) != 0 { if len(test7.Topics[2]) != 0 {
t.Fatalf("expected 0 topics, got %d topics", len(test7.Topics[2])) t.Fatalf("expected 0 topics, got %d topics", len(test7.Topics[2]))
} }
// from, to block hash
var test8 FilterCriteria
vector = fmt.Sprintf(`{"fromBlock":"%s","toBlock":"%s"}`, fromBlockHash.Hex(), toBlockHash.Hex())
if err := json.Unmarshal([]byte(vector), &test8); err != nil {
t.Fatal(err)
}
if *test8.FromBlockHash != fromBlockHash {
t.Fatalf("expected FromBlock %s, got %s", fromBlockHash.Hex(), test8.FromBlockHash.Hex())
}
if *test8.ToBlockHash != toBlockHash {
t.Fatalf("expected ToBlock %s, got %s", toBlockHash.Hex(), test8.ToBlockHash.Hex())
}
} }

View file

@ -116,6 +116,8 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
//} //}
} }
end := rawdb.ReadCanonicalHash(db, uint64(cnt*sectionSize-1))
d := time.Since(start) d := time.Since(start)
fmt.Println("Finished generating bloombits data") fmt.Println("Finished generating bloombits data")
fmt.Println(" ", d, "total ", d/time.Duration(cnt*sectionSize), "per block") fmt.Println(" ", d, "total ", d/time.Duration(cnt*sectionSize), "per block")
@ -135,7 +137,7 @@ func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
var addr common.Address var addr common.Address
addr[0] = byte(i) addr[0] = byte(i)
addr[1] = byte(i / 256) addr[1] = byte(i / 256)
filter := NewRangeFilter(backend, 0, int64(cnt*sectionSize-1), []common.Address{addr}, nil) filter := NewRangeFilter(backend, head, end, []common.Address{addr}, nil)
if _, err := filter.Logs(context.Background()); err != nil { if _, err := filter.Logs(context.Background()); err != nil {
b.Error("filter.Find error:", err) b.Error("filter.Find error:", err)
} }
@ -192,7 +194,7 @@ func BenchmarkNoBloomBits(b *testing.B) {
start := time.Now() start := time.Now()
mux := new(event.TypeMux) mux := new(event.TypeMux)
backend := &testBackend{mux, db, 0, new(event.Feed), new(event.Feed), new(event.Feed), new(event.Feed)} backend := &testBackend{mux, db, 0, new(event.Feed), new(event.Feed), new(event.Feed), new(event.Feed)}
filter := NewRangeFilter(backend, 0, int64(*headNum), []common.Address{{}}, nil) filter := NewRangeFilter(backend, rawdb.ReadCanonicalHash(db, 0), head, []common.Address{{}}, nil)
filter.Logs(context.Background()) filter.Logs(context.Background())
d := time.Since(start) d := time.Since(start)
fmt.Println("Finished running filter benchmarks") fmt.Println("Finished running filter benchmarks")

View file

@ -20,6 +20,7 @@ import (
"context" "context"
"errors" "errors"
"math/big" "math/big"
"sort"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
@ -56,14 +57,14 @@ type Filter struct {
topics [][]common.Hash topics [][]common.Hash
block common.Hash // Block hash if filtering a single block block common.Hash // Block hash if filtering a single block
begin, end int64 // Range interval if filtering multiple blocks begin, end common.Hash // Range interval if filtering multiple blocks
matcher *bloombits.Matcher matcher *bloombits.Matcher
} }
// NewRangeFilter creates a new filter which uses a bloom filter on blocks to // NewRangeFilter creates a new filter which uses a bloom filter on blocks to
// figure out whether a particular block is interesting or not. // figure out whether a particular block is interesting or not.
func NewRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *Filter { func NewRangeFilter(backend Backend, begin, end common.Hash, addresses []common.Address, topics [][]common.Hash) *Filter {
// Flatten the address and topic filter clauses into a single bloombits filter // Flatten the address and topic filter clauses into a single bloombits filter
// system. Since the bloombits are not positional, nil topics are permitted, // system. Since the bloombits are not positional, nil topics are permitted,
// which get flattened into a nil byte slice. // which get flattened into a nil byte slice.
@ -114,9 +115,61 @@ func newFilter(backend Backend, addresses []common.Address, topics [][]common.Ha
} }
} }
// findCommonAncestor returns the highest numbered block that is the ancestor of
// both `begin` and `end`. `end` must have a block number greater than or equal
// to `begin`.
func (f *Filter) findCommonAncestor(ctx context.Context, begin, end *types.Header) (*types.Header, bool, error) {
var err error
var mainChain bool
// If end is on the canonical chain, we can rewind efficiently
if header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(end.Number.Int64())); err != nil && header.Hash() == end.Hash() {
mainChain = true
end, err = f.backend.HeaderByNumber(ctx, rpc.BlockNumber(begin.Number.Int64()))
if err != nil {
return nil, false, err
}
} else {
mainChain = false
// Rewind until begin and end are at the same height
for end.Number.Cmp(begin.Number) > 0 {
end, err = f.backend.HeaderByHash(ctx, end.ParentHash)
if err != nil {
return nil, false, err
}
}
}
// Rewind both until they match
for begin.Hash() != end.Hash() {
begin, err = f.backend.HeaderByHash(ctx, begin.ParentHash)
if err != nil {
return nil, false, err
}
end, err = f.backend.HeaderByHash(ctx, end.ParentHash)
if err != nil {
return nil, false, err
}
}
return end, mainChain, nil
}
// logList allows sorting event logs by block number and log index
type logList []*types.Log
func (l logList) Len() int { return len(l) }
func (l logList) Swap(i, j int) { l[i], l[j] = l[j], l[i] }
func (l logList) Less(i, j int) bool {
return l[i].BlockNumber < l[j].BlockNumber || (l[i].BlockNumber == l[j].BlockNumber && l[i].Index < l[j].Index)
}
// Logs searches the blockchain for matching log entries, returning all from the // Logs searches the blockchain for matching log entries, returning all from the
// first block that contains matches, updating the start of the filter accordingly. // first block that contains matches, updating the start of the filter accordingly.
func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) { func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
var err error
// If we're doing singleton block filtering, execute and return // If we're doing singleton block filtering, execute and return
if f.block != (common.Hash{}) { if f.block != (common.Hash{}) {
header, err := f.backend.HeaderByHash(ctx, f.block) header, err := f.backend.HeaderByHash(ctx, f.block)
@ -128,48 +181,78 @@ func (f *Filter) Logs(ctx context.Context) ([]*types.Log, error) {
} }
return f.blockLogs(ctx, header) return f.blockLogs(ctx, header)
} }
// Figure out the limits of the filter range // Figure out the limits of the filter range
header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber) header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
if header == nil { if header == nil {
return nil, nil return nil, nil
} }
head := header.Number.Uint64()
if f.begin == -1 { begin := header
f.begin = int64(head) if f.begin != (common.Hash{}) {
begin, err = f.backend.HeaderByHash(ctx, f.begin)
if err != nil {
return nil, err
} }
end := uint64(f.end)
if f.end == -1 {
end = head
} }
end := header
if f.end != (common.Hash{}) {
end, err = f.backend.HeaderByHash(ctx, f.end)
if err != nil {
return nil, err
}
}
if end.Number.Cmp(begin.Number) < 0 {
return nil, nil
}
ancestor, mainChain, err := f.findCommonAncestor(ctx, begin, end)
if err != nil {
return nil, err
}
// Insert deletions of any reorg-ed logs
var logs []*types.Log
removed, err := f.unindexedLogs(ctx, ancestor.Hash(), begin.Hash())
if err != nil {
return nil, err
}
for _, log := range removed {
log.Removed = true
logs = append(logs, log)
}
// Gather all indexed logs, and finish with non indexed ones // Gather all indexed logs, and finish with non indexed ones
var ( if mainChain {
logs []*types.Log
err error
)
size, sections := f.backend.BloomStatus() size, sections := f.backend.BloomStatus()
if indexed := sections * size; indexed > uint64(f.begin) { if indexed := sections * size; indexed > ancestor.Number.Uint64() {
if indexed > end { if indexed > end.Number.Uint64() {
logs, err = f.indexedLogs(ctx, end) logs, err = f.indexedLogs(ctx, ancestor.Number.Uint64(), end.Number.Uint64())
} else { } else {
logs, err = f.indexedLogs(ctx, indexed-1) logs, err = f.indexedLogs(ctx, ancestor.Number.Uint64(), indexed-1)
} }
if err != nil { if err != nil {
return logs, err return logs, err
} }
} }
rest, err := f.unindexedLogs(ctx, end) }
rest, err := f.unindexedLogs(ctx, ancestor.Hash(), end.Hash())
logs = append(logs, rest...) logs = append(logs, rest...)
sort.Sort(logList(logs))
f.begin = end.Hash()
return logs, err return logs, err
} }
// indexedLogs returns the logs matching the filter criteria based on the bloom // indexedLogs returns the logs matching the filter criteria based on the bloom
// bits indexed available locally or via the network. // bits indexed available locally or via the network.
func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { func (f *Filter) indexedLogs(ctx context.Context, begin, end uint64) ([]*types.Log, error) {
// Create a matcher session and request servicing from the backend // Create a matcher session and request servicing from the backend
matches := make(chan uint64, 64) matches := make(chan uint64, 64)
session, err := f.matcher.Start(ctx, uint64(f.begin), end, matches) session, err := f.matcher.Start(ctx, begin, end, matches)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -186,18 +269,15 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, err
// Abort if all matches have been fulfilled // Abort if all matches have been fulfilled
if !ok { if !ok {
err := session.Error() err := session.Error()
if err == nil {
f.begin = int64(end) + 1
}
return logs, err return logs, err
} }
f.begin = int64(number) + 1
// Retrieve the suggested block and pull any truly matching logs // Retrieve the suggested block and pull any truly matching logs
header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number)) header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(number))
if header == nil || err != nil { if header == nil || err != nil {
return logs, err return logs, err
} }
f.begin = header.Hash()
found, err := f.checkMatches(ctx, header) found, err := f.checkMatches(ctx, header)
if err != nil { if err != nil {
return logs, err return logs, err
@ -212,11 +292,11 @@ func (f *Filter) indexedLogs(ctx context.Context, end uint64) ([]*types.Log, err
// indexedLogs returns the logs matching the filter criteria based on raw block // indexedLogs returns the logs matching the filter criteria based on raw block
// iteration and bloom matching. // iteration and bloom matching.
func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) { func (f *Filter) unindexedLogs(ctx context.Context, begin, end common.Hash) ([]*types.Log, error) {
var logs []*types.Log var logs []*types.Log
for ; f.begin <= int64(end); f.begin++ { for {
header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin)) header, err := f.backend.HeaderByHash(ctx, end)
if header == nil || err != nil { if header == nil || err != nil {
return logs, err return logs, err
} }
@ -225,6 +305,10 @@ func (f *Filter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, e
return logs, err return logs, err
} }
logs = append(logs, found...) logs = append(logs, found...)
if begin == end {
break
}
end = header.ParentHash
} }
return logs, nil return logs, nil
} }

View file

@ -92,7 +92,7 @@ func BenchmarkFilters(b *testing.B) {
} }
b.ResetTimer() b.ResetTimer()
filter := NewRangeFilter(backend, 0, -1, []common.Address{addr1, addr2, addr3, addr4}, nil) filter := NewRangeFilter(backend, genesis.Hash(), rawdb.ReadHeadBlockHash(db), []common.Address{addr1, addr2, addr3, addr4}, nil)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
logs, _ := filter.Logs(context.Background()) logs, _ := filter.Logs(context.Background())
@ -175,14 +175,16 @@ func TestFilters(t *testing.T) {
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i]) rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), receipts[i])
} }
filter := NewRangeFilter(backend, 0, -1, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}}) head := rawdb.ReadHeadBlockHash(db)
filter := NewRangeFilter(backend, genesis.Hash(), head, []common.Address{addr}, [][]common.Hash{{hash1, hash2, hash3, hash4}})
logs, _ := filter.Logs(context.Background()) logs, _ := filter.Logs(context.Background())
if len(logs) != 4 { if len(logs) != 4 {
t.Error("expected 4 log, got", len(logs)) t.Error("expected 4 log, got", len(logs))
} }
filter = NewRangeFilter(backend, 900, 999, []common.Address{addr}, [][]common.Hash{{hash3}}) filter = NewRangeFilter(backend, rawdb.ReadCanonicalHash(db, 900), rawdb.ReadCanonicalHash(db, 999), []common.Address{addr}, [][]common.Hash{{hash3}})
logs, _ = filter.Logs(context.Background()) logs, _ = filter.Logs(context.Background())
if len(logs) != 1 { if len(logs) != 1 {
t.Error("expected 1 log, got", len(logs)) t.Error("expected 1 log, got", len(logs))
@ -191,7 +193,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = NewRangeFilter(backend, 990, -1, []common.Address{addr}, [][]common.Hash{{hash3}}) filter = NewRangeFilter(backend, rawdb.ReadCanonicalHash(db, 990), head, []common.Address{addr}, [][]common.Hash{{hash3}})
logs, _ = filter.Logs(context.Background()) logs, _ = filter.Logs(context.Background())
if len(logs) != 1 { if len(logs) != 1 {
t.Error("expected 1 log, got", len(logs)) t.Error("expected 1 log, got", len(logs))
@ -200,7 +202,7 @@ func TestFilters(t *testing.T) {
t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0]) t.Errorf("expected log[0].Topics[0] to be %x, got %x", hash3, logs[0].Topics[0])
} }
filter = NewRangeFilter(backend, 1, 10, nil, [][]common.Hash{{hash1, hash2}}) filter = NewRangeFilter(backend, rawdb.ReadCanonicalHash(db, 1), rawdb.ReadCanonicalHash(db, 10), nil, [][]common.Hash{{hash1, hash2}})
logs, _ = filter.Logs(context.Background()) logs, _ = filter.Logs(context.Background())
if len(logs) != 2 { if len(logs) != 2 {
@ -208,7 +210,7 @@ func TestFilters(t *testing.T) {
} }
failHash := common.BytesToHash([]byte("fail")) failHash := common.BytesToHash([]byte("fail"))
filter = NewRangeFilter(backend, 0, -1, nil, [][]common.Hash{{failHash}}) filter = NewRangeFilter(backend, genesis.Hash(), head, nil, [][]common.Hash{{failHash}})
logs, _ = filter.Logs(context.Background()) logs, _ = filter.Logs(context.Background())
if len(logs) != 0 { if len(logs) != 0 {
@ -216,14 +218,14 @@ func TestFilters(t *testing.T) {
} }
failAddr := common.BytesToAddress([]byte("failmenow")) failAddr := common.BytesToAddress([]byte("failmenow"))
filter = NewRangeFilter(backend, 0, -1, []common.Address{failAddr}, nil) filter = NewRangeFilter(backend, genesis.Hash(), head, []common.Address{failAddr}, nil)
logs, _ = filter.Logs(context.Background()) logs, _ = filter.Logs(context.Background())
if len(logs) != 0 { if len(logs) != 0 {
t.Error("expected 0 log, got", len(logs)) t.Error("expected 0 log, got", len(logs))
} }
filter = NewRangeFilter(backend, 0, -1, nil, [][]common.Hash{{failHash}, {hash1}}) filter = NewRangeFilter(backend, genesis.Hash(), head, nil, [][]common.Hash{{failHash}, {hash1}})
logs, _ = filter.Logs(context.Background()) logs, _ = filter.Logs(context.Background())
if len(logs) != 0 { if len(logs) != 0 {

View file

@ -133,7 +133,9 @@ type ContractCaller interface {
type FilterQuery struct { type FilterQuery struct {
BlockHash *common.Hash // used by eth_getLogs, return logs only from block with this hash 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
FromBlockHash *common.Hash // beginning of the queried range, as a block hash
ToBlock *big.Int // end of the range, nil means latest block ToBlock *big.Int // end of the range, nil means latest block
ToBlockHash *common.Hash // end of the range, as a block hash
Addresses []common.Address // restricts matches to events created by specific contracts Addresses []common.Address // restricts matches to events created by specific contracts
// The Topic list restricts matches to particular event topics. Each event has a list // The Topic list restricts matches to particular event topics. Each event has a list

File diff suppressed because one or more lines are too long

View file

@ -5431,6 +5431,12 @@ var methods = function () {
params: 0 params: 0
}); });
var getLogs = new Method({
name: 'getLogs',
call: 'eth_getLogs',
params: 1
})
return [ return [
getBalance, getBalance,
getStorageAt, getStorageAt,
@ -5454,7 +5460,8 @@ var methods = function () {
compileLLL, compileLLL,
compileSerpent, compileSerpent,
submitWork, submitWork,
getWork getWork,
getLogs
]; ];
}; };

View file

@ -20,10 +20,12 @@ import (
"fmt" "fmt"
"math" "math"
"reflect" "reflect"
"strconv"
"strings" "strings"
"sync" "sync"
mapset "github.com/deckarep/golang-set" mapset "github.com/deckarep/golang-set"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
) )
@ -134,6 +136,11 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
input := strings.TrimSpace(string(data)) input := strings.TrimSpace(string(data))
if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' { if len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"' {
input = input[1 : len(input)-1] input = input[1 : len(input)-1]
} else {
// Integer block number
value, err := strconv.Atoi(string(data))
*bn = BlockNumber(value)
return err
} }
switch input { switch input {
@ -163,3 +170,34 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
func (bn BlockNumber) Int64() int64 { func (bn BlockNumber) Int64() int64 {
return (int64)(bn) return (int64)(bn)
} }
// BlockNumberOrHash permits JSON deserialization and parsing of a value that
// can be either a block number or block hash.
type BlockNumberOrHash string
// UnmarshalJSON parses the supplied JSON fragment as a BlockNumberOrHash.
func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
*bnh = BlockNumberOrHash(data)
return nil
}
// IsHash returns true iff the value is a block hash.
func (bnh BlockNumberOrHash) IsHash() bool {
return bnh[0] == '"' && bnh[len(bnh)-1] == '"' && len(bnh) == 68
}
// Hash returns the hash value, or nil if the value is not a hash.
func (bnh BlockNumberOrHash) Hash() *common.Hash {
if !bnh.IsHash() {
return nil
}
hash := common.HexToHash(string(bnh[1 : len(bnh)-1]))
return &hash
}
// Number returns the numeric value, or 0 if the value is not numeric.
func (bnh BlockNumberOrHash) Number() int64 {
var bn BlockNumber
(&bn).UnmarshalJSON([]byte(bnh))
return bn.Int64()
}