From 432749d6a1bd9e403f325c8164640f53fce4d236 Mon Sep 17 00:00:00 2001
From: Jaynti Kanani <jdkanani@gmail.com>
Date: Sun, 18 Oct 2020 16:22:38 +0530
Subject: [PATCH] new: add filter for block logs

---
 accounts/abi/bind/backends/bor_simulated.go |  31 ++++
 eth/bor_api_backend.go                      |  18 +--
 eth/filters/bor_api.go                      |  35 +++++
 eth/filters/bor_filter.go                   | 150 ++++++++++++++++++++
 eth/filters/filter.go                       |   2 +
 internal/ethapi/backend.go                  |   1 +
 les/api_backend.go                          |   4 +
 7 files changed, 230 insertions(+), 11 deletions(-)
 create mode 100644 accounts/abi/bind/backends/bor_simulated.go
 create mode 100644 eth/filters/bor_api.go
 create mode 100644 eth/filters/bor_filter.go

diff --git a/accounts/abi/bind/backends/bor_simulated.go b/accounts/abi/bind/backends/bor_simulated.go
new file mode 100644
index 000000000..3f5480d6a
--- /dev/null
+++ b/accounts/abi/bind/backends/bor_simulated.go
@@ -0,0 +1,31 @@
+package backends
+
+import (
+	"context"
+
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/core/rawdb"
+	"github.com/maticnetwork/bor/core/types"
+)
+
+func (fb *filterBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error) {
+	number := rawdb.ReadHeaderNumber(fb.db, hash)
+	if number == nil {
+		return nil, nil
+	}
+	receipt := rawdb.ReadRawBorReceipt(fb.db, hash, *number)
+	if receipt == nil {
+		return nil, nil
+	}
+
+	return receipt, nil
+}
+
+func (fb *filterBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
+	receipt, err := fb.GetBorBlockReceipt(ctx, hash)
+	if err != nil || receipt == nil {
+		return nil, err
+	}
+
+	return receipt.Logs, nil
+}
diff --git a/eth/bor_api_backend.go b/eth/bor_api_backend.go
index ac2c4ec73..5a13ea1a6 100644
--- a/eth/bor_api_backend.go
+++ b/eth/bor_api_backend.go
@@ -33,14 +33,10 @@ func (b *EthAPIBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash
 	return receipt, nil
 }
 
-// func (b *EthAPIBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
-// 	receipts := b.eth.blockchain.GetReceiptsByHash(hash)
-// 	if receipts == nil {
-// 		return nil, nil
-// 	}
-// 	logs := make([][]*types.Log, len(receipts))
-// 	for i, receipt := range receipts {
-// 		logs[i] = receipt.Logs
-// 	}
-// 	return logs, nil
-// }
+func (b *EthAPIBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
+	receipt := b.eth.blockchain.GetBorReceiptByHash(hash)
+	if receipt == nil {
+		return nil, nil
+	}
+	return receipt.Logs, nil
+}
diff --git a/eth/filters/bor_api.go b/eth/filters/bor_api.go
new file mode 100644
index 000000000..b1d82e92a
--- /dev/null
+++ b/eth/filters/bor_api.go
@@ -0,0 +1,35 @@
+package filters
+
+import (
+	"context"
+
+	"github.com/maticnetwork/bor/core/types"
+	"github.com/maticnetwork/bor/rpc"
+)
+
+func (api *PublicFilterAPI) GetBorBlockLogs(ctx context.Context, crit FilterCriteria) ([]*types.Log, error) {
+	var filter *BorBlockLogsFilter
+	if crit.BlockHash != nil {
+		// Block filter requested, construct a single-shot filter
+		filter = NewBorBlockLogsFilter(api.backend, *crit.BlockHash, crit.Addresses, crit.Topics)
+	} else {
+		// Convert the RPC block numbers into internal representations
+		begin := rpc.LatestBlockNumber.Int64()
+		if crit.FromBlock != nil {
+			begin = crit.FromBlock.Int64()
+		}
+		end := rpc.LatestBlockNumber.Int64()
+		if crit.ToBlock != nil {
+			end = crit.ToBlock.Int64()
+		}
+		// Construct the range filter
+		filter = NewBorBlockLogsRangeFilter(api.backend, begin, end, crit.Addresses, crit.Topics)
+	}
+
+	// Run the filter and return all the logs
+	logs, err := filter.Logs(ctx)
+	if err != nil {
+		return nil, err
+	}
+	return returnLogs(logs), err
+}
diff --git a/eth/filters/bor_filter.go b/eth/filters/bor_filter.go
new file mode 100644
index 000000000..6ddd8e784
--- /dev/null
+++ b/eth/filters/bor_filter.go
@@ -0,0 +1,150 @@
+// Copyright 2014 The go-ethereum Authors
+// This file is part of the go-ethereum library.
+//
+// The go-ethereum library is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Lesser General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// The go-ethereum library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Lesser General Public License for more details.
+//
+// You should have received a copy of the GNU Lesser General Public License
+// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
+
+package filters
+
+import (
+	"context"
+	"errors"
+
+	"github.com/maticnetwork/bor/common"
+	"github.com/maticnetwork/bor/core/types"
+	"github.com/maticnetwork/bor/ethdb"
+	"github.com/maticnetwork/bor/rpc"
+)
+
+// BorBlockLogsFilter can be used to retrieve and filter logs.
+type BorBlockLogsFilter struct {
+	backend Backend
+
+	db        ethdb.Database
+	addresses []common.Address
+	topics    [][]common.Hash
+
+	block      common.Hash // Block hash if filtering a single block
+	begin, end int64       // Range interval if filtering multiple blocks
+}
+
+// NewBorBlockLogsRangeFilter creates a new filter which uses a bloom filter on blocks to
+// figure out whether a particular block is interesting or not.
+func NewBorBlockLogsRangeFilter(backend Backend, begin, end int64, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
+	// Create a generic filter and convert it into a range filter
+	filter := newBorBlockLogsFilter(backend, addresses, topics)
+	filter.begin = begin
+	filter.end = end
+
+	return filter
+}
+
+// NewBorBlockLogsFilter creates a new filter which directly inspects the contents of
+// a block to figure out whether it is interesting or not.
+func NewBorBlockLogsFilter(backend Backend, block common.Hash, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
+	// Create a generic filter and convert it into a block filter
+	filter := newBorBlockLogsFilter(backend, addresses, topics)
+	filter.block = block
+	return filter
+}
+
+// newBorBlockLogsFilter 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 newBorBlockLogsFilter(backend Backend, addresses []common.Address, topics [][]common.Hash) *BorBlockLogsFilter {
+	return &BorBlockLogsFilter{
+		backend:   backend,
+		addresses: addresses,
+		topics:    topics,
+		db:        backend.ChainDb(),
+	}
+}
+
+// Logs searches the blockchain for matching log entries, returning all from the
+// first block that contains matches, updating the start of the filter accordingly.
+func (f *BorBlockLogsFilter) Logs(ctx context.Context) ([]*types.Log, error) {
+	// If we're doing singleton block filtering, execute and return
+	if f.block != (common.Hash{}) {
+		receipt, err := f.backend.GetBorBlockReceipt(ctx, f.block)
+		if err != nil {
+			return nil, err
+		}
+
+		if receipt == nil {
+			return nil, errors.New("unknown block")
+		}
+		return f.borBlockLogs(ctx, receipt)
+	}
+
+	// Figure out the limits of the filter range
+	header, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
+	if header == nil {
+		return nil, nil
+	}
+	head := header.Number.Uint64()
+
+	if f.begin == -1 {
+		f.begin = int64(nextSprintEnd(head))
+	}
+	end := f.end
+	if f.end == -1 {
+		end = int64(head)
+	}
+
+	// Gather all indexed logs, and finish with non indexed ones
+	return f.unindexedLogs(ctx, uint64(end))
+}
+
+// unindexedLogs returns the logs matching the filter criteria based on raw block
+// iteration and bloom matching.
+func (f *BorBlockLogsFilter) unindexedLogs(ctx context.Context, end uint64) ([]*types.Log, error) {
+	var logs []*types.Log
+
+	for ; f.begin <= int64(end); f.begin = f.begin + 64 {
+		header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(f.begin))
+		if header == nil || err != nil {
+			return logs, err
+		}
+
+		// get bor block receipt
+		receipt, err := f.backend.GetBorBlockReceipt(ctx, header.Hash())
+		if receipt == nil || err != nil {
+			continue
+		}
+
+		// filter bor block logs
+		found, err := f.borBlockLogs(ctx, receipt)
+		if err != nil {
+			return logs, err
+		}
+		logs = append(logs, found...)
+	}
+	return logs, nil
+}
+
+// borBlockLogs returns the logs matching the filter criteria within a single block.
+func (f *BorBlockLogsFilter) borBlockLogs(ctx context.Context, receipt *types.BorReceipt) (logs []*types.Log, err error) {
+	if bloomFilter(receipt.Bloom, f.addresses, f.topics) {
+		found := filterLogs(receipt.Logs, nil, nil, f.addresses, f.topics)
+		logs = append(logs, found...)
+	}
+	return logs, nil
+}
+
+func nextSprintEnd(n uint64) uint64 {
+	m := n % 64
+	if m == 0 {
+		return n
+	}
+
+	return n + 64 - m
+}
diff --git a/eth/filters/filter.go b/eth/filters/filter.go
index e14303574..1b91e06ed 100644
--- a/eth/filters/filter.go
+++ b/eth/filters/filter.go
@@ -36,6 +36,8 @@ type Backend interface {
 	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)
+	GetBorBlockReceipt(ctx context.Context, blockHash common.Hash) (*types.BorReceipt, error)
+	GetBorBlockLogs(ctx context.Context, blockHash common.Hash) ([]*types.Log, error)
 
 	SubscribeNewTxsEvent(chan<- core.NewTxsEvent) event.Subscription
 	SubscribeChainEvent(ch chan<- core.ChainEvent) event.Subscription
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 272aea858..a8a6c4b9e 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -69,6 +69,7 @@ type Backend interface {
 	// Bor API
 	GetRootHash(ctx context.Context, starBlockNr uint64, endBlockNr uint64) (string, error)
 	GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error)
+	GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error)
 
 	// Transaction pool API
 	SendTx(ctx context.Context, signedTx *types.Transaction) error
diff --git a/les/api_backend.go b/les/api_backend.go
index 86dc91ea2..1f0dc8abf 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -291,3 +291,7 @@ func (b *LesApiBackend) GetRootHash(ctx context.Context, starBlockNr uint64, end
 func (b *LesApiBackend) GetBorBlockReceipt(ctx context.Context, hash common.Hash) (*types.BorReceipt, error) {
 	return nil, errors.New("Not implemented")
 }
+
+func (b *LesApiBackend) GetBorBlockLogs(ctx context.Context, hash common.Hash) ([]*types.Log, error) {
+	return nil, errors.New("Not implemented")
+}
-- 
GitLab