diff --git a/core/blockchain_bor_test.go b/core/blockchain_bor_test.go new file mode 100644 index 0000000000000000000000000000000000000000..c78240b9d865099f17a3642ee96a5b105b2f0b4b --- /dev/null +++ b/core/blockchain_bor_test.go @@ -0,0 +1,144 @@ +package core + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/consensus/ethash" + "github.com/ethereum/go-ethereum/core/rawdb" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/core/vm" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/params" +) + +func TestChain2HeadEvent(t *testing.T) { + var ( + db = rawdb.NewMemoryDatabase() + key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") + addr1 = crypto.PubkeyToAddress(key1.PublicKey) + gspec = &Genesis{ + Config: params.TestChainConfig, + Alloc: GenesisAlloc{addr1: {Balance: big.NewInt(10000000000000000)}}, + } + genesis = gspec.MustCommit(db) + signer = types.LatestSigner(gspec.Config) + ) + + blockchain, _ := NewBlockChain(db, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil) + defer blockchain.Stop() + + chain, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 3, func(i int, gen *BlockGen) {}) + if _, err := blockchain.InsertChain(chain); err != nil { + t.Fatalf("failed to insert chain: %v", err) + } + + replacementBlocks, _ := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), db, 4, func(i int, gen *BlockGen) { + tx, err := types.SignTx(types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), 1000000, gen.header.BaseFee, nil), signer, key1) + if i == 2 { + gen.OffsetTime(-9) + } + if err != nil { + t.Fatalf("failed to create tx: %v", err) + } + gen.AddTx(tx) + }) + chain2HeadCh := make(chan Chain2HeadEvent, 64) + blockchain.SubscribeChain2HeadEvent(chain2HeadCh) + if _, err := blockchain.InsertChain(replacementBlocks); err != nil { + t.Fatalf("failed to insert chain: %v", err) + } + + // first two block of the secondary chain are for a brief moment considered + // side chains because up to that point the first one is considered the + // heavier chain. + expectedReorgHashes := map[common.Hash]bool{ + replacementBlocks[0].Hash(): true, + replacementBlocks[1].Hash(): true, + replacementBlocks[2].Hash(): true, + } + + expectedReplacedHashes := map[common.Hash]bool{ + chain[0].Hash(): true, + chain[1].Hash(): true, + chain[2].Hash(): true, + } + + expectedForkHashes := map[common.Hash]bool{ + replacementBlocks[0].Hash(): true, + replacementBlocks[1].Hash(): true, + } + + expectedHeadHashes := map[common.Hash]bool{ + replacementBlocks[3].Hash(): true, + } + i := 0 + + //number of totalEvents are 4 : when the second chain is generated, there are 2 fork events, + //then reorg happens and triggers 1 event, then last head block of Replacement chain triggers 1 event + totalEvents := 4 + + const timeoutDura = 10 * time.Second + timeout := time.NewTimer(timeoutDura) +done: + for { + select { + case ev := <-chain2HeadCh: + if ev.Type == Chain2HeadReorgEvent { + //Reorg Event Sends Chain of Added Blocks in NewChain. So need to check all of them in reorgHashes + for j := 0; j < len(ev.OldChain); j++ { + block := ev.OldChain[j] + if _, ok := expectedReplacedHashes[block.Hash()]; !ok { + t.Errorf("%d: didn't expect %x to be in side chain", i, block.Hash()) + } + + } + //Reorg Event also Sends Chain of Removed Blocks in NewChain. So need to check all of them in replacedHashes + for j := 0; j < len(ev.OldChain); j++ { + block := ev.NewChain[j] + if _, ok := expectedReorgHashes[block.Hash()]; !ok { + t.Errorf("%d: didn't expect %x to be in side chain", i, block.Hash()) + } + + } + + } + + if ev.Type == Chain2HeadForkEvent { + block := ev.NewChain[0] + if _, ok := expectedForkHashes[block.Hash()]; !ok { + t.Errorf("%d: didn't expect %x to be in fork chain", i, block.Hash()) + } + } + + if ev.Type == Chain2HeadCanonicalEvent { + block := ev.NewChain[0] + if _, ok := expectedHeadHashes[block.Hash()]; !ok { + t.Errorf("%d: didn't expect %x to be in head chain", i, block.Hash()) + } + } + + i++ + + if i == (totalEvents) { + timeout.Stop() + + break done + } + timeout.Reset(timeoutDura) + + case <-timeout.C: + t.Fatal("Timeout. Possibly not all blocks were triggered for sideevent") + } + } + + // make sure no more events are fired + select { + case e := <-chain2HeadCh: + t.Errorf("unexpected event fired: %v", e) + case <-time.After(250 * time.Millisecond): + } + +}