|
|
@@ -22,6 +22,7 @@ import (
|
|
|
"math/big"
|
|
|
"math/rand"
|
|
|
"reflect"
|
|
|
+ "runtime"
|
|
|
"testing"
|
|
|
"time"
|
|
|
|
|
|
@@ -38,6 +39,10 @@ import (
|
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
|
)
|
|
|
|
|
|
+var (
|
|
|
+ deadline = 5 * time.Minute
|
|
|
+)
|
|
|
+
|
|
|
type testBackend struct {
|
|
|
mux *event.TypeMux
|
|
|
db ethdb.Database
|
|
|
@@ -163,7 +168,7 @@ func TestBlockSubscription(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
genesis = new(core.Genesis).MustCommit(db)
|
|
|
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 10, func(i int, gen *core.BlockGen) {})
|
|
|
chainEvents = []core.ChainEvent{}
|
|
|
@@ -215,7 +220,7 @@ func TestPendingTxFilter(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
|
|
|
transactions = []*types.Transaction{
|
|
|
types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil),
|
|
|
@@ -270,7 +275,7 @@ func TestLogFilterCreation(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
|
|
|
testCases = []struct {
|
|
|
crit FilterCriteria
|
|
|
@@ -314,7 +319,7 @@ func TestInvalidLogFilterCreation(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
)
|
|
|
|
|
|
// different situations where log filter creation should fail.
|
|
|
@@ -336,7 +341,7 @@ func TestInvalidGetLogsRequest(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
blockHash = common.HexToHash("0x1111111111111111111111111111111111111111111111111111111111111111")
|
|
|
)
|
|
|
|
|
|
@@ -361,7 +366,7 @@ func TestLogFilter(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
|
|
|
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
|
|
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
|
|
@@ -475,7 +480,7 @@ func TestPendingLogsSubscription(t *testing.T) {
|
|
|
var (
|
|
|
db = rawdb.NewMemoryDatabase()
|
|
|
backend = &testBackend{db: db}
|
|
|
- api = NewPublicFilterAPI(backend, false)
|
|
|
+ api = NewPublicFilterAPI(backend, false, deadline)
|
|
|
|
|
|
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
|
|
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
|
|
@@ -601,6 +606,73 @@ func TestPendingLogsSubscription(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// TestPendingTxFilterDeadlock tests if the event loop hangs when pending
|
|
|
+// txes arrive at the same time that one of multiple filters is timing out.
|
|
|
+// Please refer to #22131 for more details.
|
|
|
+func TestPendingTxFilterDeadlock(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ timeout := 100 * time.Millisecond
|
|
|
+
|
|
|
+ var (
|
|
|
+ db = rawdb.NewMemoryDatabase()
|
|
|
+ backend = &testBackend{db: db}
|
|
|
+ api = NewPublicFilterAPI(backend, false, timeout)
|
|
|
+ done = make(chan struct{})
|
|
|
+ )
|
|
|
+
|
|
|
+ go func() {
|
|
|
+ // Bombard feed with txes until signal was received to stop
|
|
|
+ i := uint64(0)
|
|
|
+ for {
|
|
|
+ select {
|
|
|
+ case <-done:
|
|
|
+ return
|
|
|
+ default:
|
|
|
+ }
|
|
|
+
|
|
|
+ tx := types.NewTransaction(i, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), 0, new(big.Int), nil)
|
|
|
+ backend.txFeed.Send(core.NewTxsEvent{Txs: []*types.Transaction{tx}})
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ }()
|
|
|
+
|
|
|
+ // Create a bunch of filters that will
|
|
|
+ // timeout either in 100ms or 200ms
|
|
|
+ fids := make([]rpc.ID, 20)
|
|
|
+ for i := 0; i < len(fids); i++ {
|
|
|
+ fid := api.NewPendingTransactionFilter()
|
|
|
+ fids[i] = fid
|
|
|
+ // Wait for at least one tx to arrive in filter
|
|
|
+ for {
|
|
|
+ hashes, err := api.GetFilterChanges(fid)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("Filter should exist: %v\n", err)
|
|
|
+ }
|
|
|
+ if len(hashes.([]common.Hash)) > 0 {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ runtime.Gosched()
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Wait until filters have timed out
|
|
|
+ time.Sleep(3 * timeout)
|
|
|
+
|
|
|
+ // If tx loop doesn't consume `done` after a second
|
|
|
+ // it's hanging.
|
|
|
+ select {
|
|
|
+ case done <- struct{}{}:
|
|
|
+ // Check that all filters have been uninstalled
|
|
|
+ for _, fid := range fids {
|
|
|
+ if _, err := api.GetFilterChanges(fid); err == nil {
|
|
|
+ t.Errorf("Filter %s should have been uninstalled\n", fid)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case <-time.After(1 * time.Second):
|
|
|
+ t.Error("Tx sending loop hangs")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
func flattenLogs(pl [][]*types.Log) []*types.Log {
|
|
|
var logs []*types.Log
|
|
|
for _, l := range pl {
|