Commit fc7e0fe6 authored by gary rong's avatar gary rong Committed by Felix Lange

core, miner: remove PostChainEvents (#19396)

This change:

- removes the PostChainEvents method on core.BlockChain.
- sorts 'removed log' events by block number.
- fire the NewChainHead event if we inject a canonical block into the chain
  even if the entire insertion is not successful.
- guarantees correct event ordering in all cases.
parent 5cc6e7a7
This diff is collapsed.
...@@ -22,6 +22,7 @@ import ( ...@@ -22,6 +22,7 @@ import (
"math/big" "math/big"
"math/rand" "math/rand"
"os" "os"
"reflect"
"sync" "sync"
"testing" "testing"
"time" "time"
...@@ -960,16 +961,20 @@ func TestLogReorgs(t *testing.T) { ...@@ -960,16 +961,20 @@ func TestLogReorgs(t *testing.T) {
} }
chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) chain, _ = GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {})
done := make(chan struct{})
go func() {
ev := <-rmLogsCh
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
close(done)
}()
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err) t.Fatalf("failed to insert forked chain: %v", err)
} }
timeout := time.NewTimer(1 * time.Second) timeout := time.NewTimer(1 * time.Second)
select { select {
case ev := <-rmLogsCh: case <-done:
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
case <-timeout.C: case <-timeout.C:
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.") t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
} }
...@@ -982,39 +987,47 @@ func TestLogRebirth(t *testing.T) { ...@@ -982,39 +987,47 @@ func TestLogRebirth(t *testing.T) {
db = rawdb.NewMemoryDatabase() db = rawdb.NewMemoryDatabase()
// this code generates a log // this code generates a log
code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00") code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}} gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
genesis = gspec.MustCommit(db) genesis = gspec.MustCommit(db)
signer = types.NewEIP155Signer(gspec.Config.ChainID) signer = types.NewEIP155Signer(gspec.Config.ChainID)
newLogCh = make(chan bool) newLogCh = make(chan bool)
removeLogCh = make(chan bool)
) )
// listenNewLog checks whether the received logs number is equal with expected. // validateLogEvent checks whether the received logs number is equal with expected.
listenNewLog := func(sink chan []*types.Log, expect int) { validateLogEvent := func(sink interface{}, result chan bool, expect int) {
chanval := reflect.ValueOf(sink)
chantyp := chanval.Type()
if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.RecvDir == 0 {
t.Fatalf("invalid channel, given type %v", chantyp)
}
cnt := 0 cnt := 0
var recv []reflect.Value
timeout := time.After(1 * time.Second)
cases := []reflect.SelectCase{{Chan: chanval, Dir: reflect.SelectRecv}, {Chan: reflect.ValueOf(timeout), Dir: reflect.SelectRecv}}
for { for {
select { chose, v, _ := reflect.Select(cases)
case logs := <-sink: if chose == 1 {
cnt += len(logs) // Not enough event received
case <-time.NewTimer(5 * time.Second).C: result <- false
// new logs timeout
newLogCh <- false
return return
} }
cnt += 1
recv = append(recv, v)
if cnt == expect { if cnt == expect {
break break
} else if cnt > expect {
// redundant logs received
newLogCh <- false
return
} }
} }
select { done := time.After(50 * time.Millisecond)
case <-sink: cases = cases[:1]
// redundant logs received cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
newLogCh <- false chose, _, _ := reflect.Select(cases)
case <-time.NewTimer(100 * time.Millisecond).C: // If chose equal 0, it means receiving redundant events.
newLogCh <- true if chose == 1 {
result <- true
} else {
result <- false
} }
} }
...@@ -1038,12 +1051,12 @@ func TestLogRebirth(t *testing.T) { ...@@ -1038,12 +1051,12 @@ func TestLogRebirth(t *testing.T) {
}) })
// Spawn a goroutine to receive log events // Spawn a goroutine to receive log events
go listenNewLog(logsCh, 1) go validateLogEvent(logsCh, newLogCh, 1)
if _, err := blockchain.InsertChain(chain); err != nil { if _, err := blockchain.InsertChain(chain); err != nil {
t.Fatalf("failed to insert chain: %v", err) t.Fatalf("failed to insert chain: %v", err)
} }
if !<-newLogCh { if !<-newLogCh {
t.Fatalf("failed to receive new log event") t.Fatal("failed to receive new log event")
} }
// Generate long reorg chain // Generate long reorg chain
...@@ -1060,40 +1073,31 @@ func TestLogRebirth(t *testing.T) { ...@@ -1060,40 +1073,31 @@ func TestLogRebirth(t *testing.T) {
}) })
// Spawn a goroutine to receive log events // Spawn a goroutine to receive log events
go listenNewLog(logsCh, 1) go validateLogEvent(logsCh, newLogCh, 1)
go validateLogEvent(rmLogsCh, removeLogCh, 1)
if _, err := blockchain.InsertChain(forkChain); err != nil { if _, err := blockchain.InsertChain(forkChain); err != nil {
t.Fatalf("failed to insert forked chain: %v", err) t.Fatalf("failed to insert forked chain: %v", err)
} }
if !<-newLogCh { if !<-newLogCh {
t.Fatalf("failed to receive new log event") t.Fatal("failed to receive new log event")
} }
// Ensure removedLog events received if !<-removeLogCh {
select { t.Fatal("failed to receive removed log event")
case ev := <-rmLogsCh:
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
case <-time.NewTimer(1 * time.Second).C:
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
} }
newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {}) newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
go listenNewLog(logsCh, 1) go validateLogEvent(logsCh, newLogCh, 1)
go validateLogEvent(rmLogsCh, removeLogCh, 1)
if _, err := blockchain.InsertChain(newBlocks); err != nil { if _, err := blockchain.InsertChain(newBlocks); err != nil {
t.Fatalf("failed to insert forked chain: %v", err) t.Fatalf("failed to insert forked chain: %v", err)
} }
// Ensure removedLog events received
select {
case ev := <-rmLogsCh:
if len(ev.Logs) == 0 {
t.Error("expected logs")
}
case <-time.NewTimer(1 * time.Second).C:
t.Fatal("Timeout. There is no RemovedLogsEvent has been sent.")
}
// Rebirth logs should omit a newLogEvent // Rebirth logs should omit a newLogEvent
if !<-newLogCh { if !<-newLogCh {
t.Fatalf("failed to receive new log event") t.Fatal("failed to receive new log event")
}
// Ensure removedLog events received
if !<-removeLogCh {
t.Fatal("failed to receive removed log event")
} }
} }
...@@ -1145,7 +1149,6 @@ func TestSideLogRebirth(t *testing.T) { ...@@ -1145,7 +1149,6 @@ func TestSideLogRebirth(t *testing.T) {
logsCh := make(chan []*types.Log) logsCh := make(chan []*types.Log)
blockchain.SubscribeLogsEvent(logsCh) blockchain.SubscribeLogsEvent(logsCh)
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
if i == 1 { if i == 1 {
// Higher block difficulty // Higher block difficulty
......
...@@ -590,7 +590,7 @@ func (w *worker) resultLoop() { ...@@ -590,7 +590,7 @@ func (w *worker) resultLoop() {
logs = append(logs, receipt.Logs...) logs = append(logs, receipt.Logs...)
} }
// Commit block and state to database. // Commit block and state to database.
stat, err := w.chain.WriteBlockWithState(block, receipts, task.state) _, err := w.chain.WriteBlockWithState(block, receipts, logs, task.state, true)
if err != nil { if err != nil {
log.Error("Failed writing block to chain", "err", err) log.Error("Failed writing block to chain", "err", err)
continue continue
...@@ -601,16 +601,6 @@ func (w *worker) resultLoop() { ...@@ -601,16 +601,6 @@ func (w *worker) resultLoop() {
// Broadcast the block and announce chain insertion event // Broadcast the block and announce chain insertion event
w.mux.Post(core.NewMinedBlockEvent{Block: block}) w.mux.Post(core.NewMinedBlockEvent{Block: block})
var events []interface{}
switch stat {
case core.CanonStatTy:
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
events = append(events, core.ChainHeadEvent{Block: block})
case core.SideStatTy:
events = append(events, core.ChainSideEvent{Block: block})
}
w.chain.PostChainEvents(events, logs)
// Insert the block into the set of pending ones to resultLoop for confirmations // Insert the block into the set of pending ones to resultLoop for confirmations
w.unconfirmed.Insert(block.NumberU64(), block.Hash()) w.unconfirmed.Insert(block.NumberU64(), block.Hash())
...@@ -996,3 +986,11 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st ...@@ -996,3 +986,11 @@ func (w *worker) commit(uncles []*types.Header, interval func(), update bool, st
} }
return nil return nil
} }
// postSideBlock fires a side chain event, only use it for testing.
func (w *worker) postSideBlock(event core.ChainSideEvent) {
select {
case w.chainSideCh <- event:
case <-w.exitCh:
}
}
...@@ -149,9 +149,6 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine ...@@ -149,9 +149,6 @@ func newTestWorkerBackend(t *testing.T, chainConfig *params.ChainConfig, engine
func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain } func (b *testWorkerBackend) BlockChain() *core.BlockChain { return b.chain }
func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool } func (b *testWorkerBackend) TxPool() *core.TxPool { return b.txPool }
func (b *testWorkerBackend) PostChainEvents(events []interface{}) {
b.chain.PostChainEvents(events, nil)
}
func (b *testWorkerBackend) newRandomUncle() *types.Block { func (b *testWorkerBackend) newRandomUncle() *types.Block {
var parent *types.Block var parent *types.Block
...@@ -243,8 +240,8 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) { ...@@ -243,8 +240,8 @@ func testGenerateBlockAndImport(t *testing.T, isClique bool) {
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
b.txPool.AddLocal(b.newRandomTx(true)) b.txPool.AddLocal(b.newRandomTx(true))
b.txPool.AddLocal(b.newRandomTx(false)) b.txPool.AddLocal(b.newRandomTx(false))
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.newRandomUncle()}}) w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.newRandomUncle()}}) w.postSideBlock(core.ChainSideEvent{Block: b.newRandomUncle()})
select { select {
case e := <-loopErr: case e := <-loopErr:
t.Fatal(e) t.Fatal(e)
...@@ -295,7 +292,7 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens ...@@ -295,7 +292,7 @@ func testEmptyWork(t *testing.T, chainConfig *params.ChainConfig, engine consens
} }
w.skipSealHook = func(task *task) bool { return true } w.skipSealHook = func(task *task) bool { return true }
w.fullTaskHook = func() { w.fullTaskHook = func() {
// Aarch64 unit tests are running in a VM on travis, they must // Arch64 unit tests are running in a VM on travis, they must
// be given more time to execute. // be given more time to execute.
time.Sleep(time.Second) time.Sleep(time.Second)
} }
...@@ -351,7 +348,8 @@ func TestStreamUncleBlock(t *testing.T) { ...@@ -351,7 +348,8 @@ func TestStreamUncleBlock(t *testing.T) {
} }
} }
b.PostChainEvents([]interface{}{core.ChainSideEvent{Block: b.uncleBlock}}) w.postSideBlock(core.ChainSideEvent{Block: b.uncleBlock})
select { select {
case <-taskCh: case <-taskCh:
case <-time.NewTimer(time.Second).C: case <-time.NewTimer(time.Second).C:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment