|
|
|
@ -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 { |
|
|
|
|