|
|
@@ -22,7 +22,6 @@ import (
|
|
|
"math/big"
|
|
|
"math/rand"
|
|
|
"os"
|
|
|
- "reflect"
|
|
|
"sync"
|
|
|
"testing"
|
|
|
"time"
|
|
|
@@ -981,189 +980,106 @@ func TestLogReorgs(t *testing.T) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// This EVM code generates a log when the contract is created.
|
|
|
+var logCode = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
|
|
+
|
|
|
+// This test checks that log events and RemovedLogsEvent are sent
|
|
|
+// when the chain reorganizes.
|
|
|
func TestLogRebirth(t *testing.T) {
|
|
|
var (
|
|
|
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
|
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
|
- db = rawdb.NewMemoryDatabase()
|
|
|
-
|
|
|
- // this code generates a log
|
|
|
- code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
|
|
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
|
|
- genesis = gspec.MustCommit(db)
|
|
|
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
|
|
- newLogCh = make(chan bool)
|
|
|
- removeLogCh = make(chan bool)
|
|
|
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
|
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
|
+ db = rawdb.NewMemoryDatabase()
|
|
|
+ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
|
|
+ genesis = gspec.MustCommit(db)
|
|
|
+ signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
|
|
+ engine = ethash.NewFaker()
|
|
|
+ blockchain, _ = NewBlockChain(db, nil, gspec.Config, engine, vm.Config{}, nil)
|
|
|
)
|
|
|
-
|
|
|
- // validateLogEvent checks whether the received logs number is equal with expected.
|
|
|
- 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
|
|
|
- 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 {
|
|
|
- chose, v, _ := reflect.Select(cases)
|
|
|
- if chose == 1 {
|
|
|
- // Not enough event received
|
|
|
- result <- false
|
|
|
- return
|
|
|
- }
|
|
|
- cnt += 1
|
|
|
- recv = append(recv, v)
|
|
|
- if cnt == expect {
|
|
|
- break
|
|
|
- }
|
|
|
- }
|
|
|
- done := time.After(50 * time.Millisecond)
|
|
|
- cases = cases[:1]
|
|
|
- cases = append(cases, reflect.SelectCase{Chan: reflect.ValueOf(done), Dir: reflect.SelectRecv})
|
|
|
- chose, _, _ := reflect.Select(cases)
|
|
|
- // If chose equal 0, it means receiving redundant events.
|
|
|
- if chose == 1 {
|
|
|
- result <- true
|
|
|
- } else {
|
|
|
- result <- false
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
|
|
defer blockchain.Stop()
|
|
|
|
|
|
- logsCh := make(chan []*types.Log)
|
|
|
- blockchain.SubscribeLogsEvent(logsCh)
|
|
|
-
|
|
|
- rmLogsCh := make(chan RemovedLogsEvent)
|
|
|
+ // The event channels.
|
|
|
+ newLogCh := make(chan []*types.Log, 10)
|
|
|
+ rmLogsCh := make(chan RemovedLogsEvent, 10)
|
|
|
+ blockchain.SubscribeLogsEvent(newLogCh)
|
|
|
blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
|
|
|
|
|
- chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
|
|
+ // This chain contains a single log.
|
|
|
+ chain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) {
|
|
|
if i == 1 {
|
|
|
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
|
|
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), logCode), signer, key1)
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
|
}
|
|
|
gen.AddTx(tx)
|
|
|
}
|
|
|
})
|
|
|
-
|
|
|
- // Spawn a goroutine to receive log events
|
|
|
- go validateLogEvent(logsCh, newLogCh, 1)
|
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
|
t.Fatalf("failed to insert chain: %v", err)
|
|
|
}
|
|
|
- if !<-newLogCh {
|
|
|
- t.Fatal("failed to receive new log event")
|
|
|
- }
|
|
|
+ checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
|
|
|
|
|
- // Generate long reorg chain
|
|
|
- forkChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
|
|
+ // Generate long reorg chain containing another log. Inserting the
|
|
|
+ // chain removes one log and adds one.
|
|
|
+ forkChain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 2, func(i int, gen *BlockGen) {
|
|
|
if i == 1 {
|
|
|
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
|
|
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), logCode), signer, key1)
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
|
}
|
|
|
gen.AddTx(tx)
|
|
|
- // Higher block difficulty
|
|
|
- gen.OffsetTime(-9)
|
|
|
+ gen.OffsetTime(-9) // higher block difficulty
|
|
|
}
|
|
|
})
|
|
|
-
|
|
|
- // Spawn a goroutine to receive log events
|
|
|
- go validateLogEvent(logsCh, newLogCh, 1)
|
|
|
- go validateLogEvent(rmLogsCh, removeLogCh, 1)
|
|
|
if _, err := blockchain.InsertChain(forkChain); err != nil {
|
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
|
}
|
|
|
- if !<-newLogCh {
|
|
|
- t.Fatal("failed to receive new log event")
|
|
|
- }
|
|
|
- if !<-removeLogCh {
|
|
|
- t.Fatal("failed to receive removed log event")
|
|
|
- }
|
|
|
+ checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
|
|
|
|
|
|
- newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
|
|
- go validateLogEvent(logsCh, newLogCh, 1)
|
|
|
- go validateLogEvent(rmLogsCh, removeLogCh, 1)
|
|
|
+ // This chain segment is rooted in the original chain, but doesn't contain any logs.
|
|
|
+ // When inserting it, the canonical chain switches away from forkChain and re-emits
|
|
|
+ // the log event for the old chain, as well as a RemovedLogsEvent for forkChain.
|
|
|
+ newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-1], engine, db, 1, func(i int, gen *BlockGen) {})
|
|
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
|
}
|
|
|
- // Rebirth logs should omit a newLogEvent
|
|
|
- if !<-newLogCh {
|
|
|
- t.Fatal("failed to receive new log event")
|
|
|
- }
|
|
|
- // Ensure removedLog events received
|
|
|
- if !<-removeLogCh {
|
|
|
- t.Fatal("failed to receive removed log event")
|
|
|
- }
|
|
|
+ checkLogEvents(t, newLogCh, rmLogsCh, 1, 1)
|
|
|
}
|
|
|
|
|
|
+// This test is a variation of TestLogRebirth. It verifies that log events are emitted
|
|
|
+// when a side chain containing log events overtakes the canonical chain.
|
|
|
func TestSideLogRebirth(t *testing.T) {
|
|
|
var (
|
|
|
- key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
|
- addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
|
- db = rawdb.NewMemoryDatabase()
|
|
|
-
|
|
|
- // this code generates a log
|
|
|
- code = common.Hex2Bytes("60606040525b7f24ec1d3ff24c2f6ff210738839dbc339cd45a5294d85c79361016243157aae7b60405180905060405180910390a15b600a8060416000396000f360606040526008565b00")
|
|
|
- gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
|
|
- genesis = gspec.MustCommit(db)
|
|
|
- signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
|
|
- newLogCh = make(chan bool)
|
|
|
+ key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
|
|
+ addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
|
|
+ db = rawdb.NewMemoryDatabase()
|
|
|
+ gspec = &Genesis{Config: params.TestChainConfig, Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000)}}}
|
|
|
+ genesis = gspec.MustCommit(db)
|
|
|
+ signer = types.NewEIP155Signer(gspec.Config.ChainID)
|
|
|
+ blockchain, _ = NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
|
|
)
|
|
|
-
|
|
|
- // listenNewLog checks whether the received logs number is equal with expected.
|
|
|
- listenNewLog := func(sink chan []*types.Log, expect int) {
|
|
|
- cnt := 0
|
|
|
- for {
|
|
|
- select {
|
|
|
- case logs := <-sink:
|
|
|
- cnt += len(logs)
|
|
|
- case <-time.NewTimer(5 * time.Second).C:
|
|
|
- // new logs timeout
|
|
|
- newLogCh <- false
|
|
|
- return
|
|
|
- }
|
|
|
- if cnt == expect {
|
|
|
- break
|
|
|
- } else if cnt > expect {
|
|
|
- // redundant logs received
|
|
|
- newLogCh <- false
|
|
|
- return
|
|
|
- }
|
|
|
- }
|
|
|
- select {
|
|
|
- case <-sink:
|
|
|
- // redundant logs received
|
|
|
- newLogCh <- false
|
|
|
- case <-time.NewTimer(100 * time.Millisecond).C:
|
|
|
- newLogCh <- true
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil)
|
|
|
defer blockchain.Stop()
|
|
|
|
|
|
- logsCh := make(chan []*types.Log)
|
|
|
- blockchain.SubscribeLogsEvent(logsCh)
|
|
|
+ newLogCh := make(chan []*types.Log, 10)
|
|
|
+ rmLogsCh := make(chan RemovedLogsEvent, 10)
|
|
|
+ blockchain.SubscribeLogsEvent(newLogCh)
|
|
|
+ blockchain.SubscribeRemovedLogsEvent(rmLogsCh)
|
|
|
+
|
|
|
chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
|
|
if i == 1 {
|
|
|
- // Higher block difficulty
|
|
|
- gen.OffsetTime(-9)
|
|
|
+ gen.OffsetTime(-9) // higher block difficulty
|
|
|
+
|
|
|
}
|
|
|
})
|
|
|
if _, err := blockchain.InsertChain(chain); err != nil {
|
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
|
}
|
|
|
+ checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
|
|
|
|
|
// Generate side chain with lower difficulty
|
|
|
sideChain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) {
|
|
|
if i == 1 {
|
|
|
- tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), code), signer, key1)
|
|
|
+ tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, new(big.Int), logCode), signer, key1)
|
|
|
if err != nil {
|
|
|
t.Fatalf("failed to create tx: %v", err)
|
|
|
}
|
|
|
@@ -1173,16 +1089,31 @@ func TestSideLogRebirth(t *testing.T) {
|
|
|
if _, err := blockchain.InsertChain(sideChain); err != nil {
|
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
|
}
|
|
|
+ checkLogEvents(t, newLogCh, rmLogsCh, 0, 0)
|
|
|
|
|
|
- // Generate a new block based on side chain
|
|
|
+ // Generate a new block based on side chain.
|
|
|
newBlocks, _ := GenerateChain(params.TestChainConfig, sideChain[len(sideChain)-1], ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
|
|
- go listenNewLog(logsCh, 1)
|
|
|
if _, err := blockchain.InsertChain(newBlocks); err != nil {
|
|
|
t.Fatalf("failed to insert forked chain: %v", err)
|
|
|
}
|
|
|
- // Rebirth logs should omit a newLogEvent
|
|
|
- if !<-newLogCh {
|
|
|
- t.Fatalf("failed to receive new log event")
|
|
|
+ checkLogEvents(t, newLogCh, rmLogsCh, 1, 0)
|
|
|
+}
|
|
|
+
|
|
|
+func checkLogEvents(t *testing.T, logsCh <-chan []*types.Log, rmLogsCh <-chan RemovedLogsEvent, wantNew, wantRemoved int) {
|
|
|
+ t.Helper()
|
|
|
+
|
|
|
+ if len(logsCh) != wantNew {
|
|
|
+ t.Fatalf("wrong number of log events: got %d, want %d", len(logsCh), wantNew)
|
|
|
+ }
|
|
|
+ if len(rmLogsCh) != wantRemoved {
|
|
|
+ t.Fatalf("wrong number of removed log events: got %d, want %d", len(rmLogsCh), wantRemoved)
|
|
|
+ }
|
|
|
+ // Drain events.
|
|
|
+ for i := 0; i < len(logsCh); i++ {
|
|
|
+ <-logsCh
|
|
|
+ }
|
|
|
+ for i := 0; i < len(rmLogsCh); i++ {
|
|
|
+ <-rmLogsCh
|
|
|
}
|
|
|
}
|
|
|
|