core: re-omit new log event when logs rebirth
This commit is contained in:
		
							parent
							
								
									d5cae48bae
								
							
						
					
					
						commit
						690bd8a417
					
				| @ -1475,11 +1475,11 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 		commonBlock *types.Block | ||||
| 		deletedTxs  types.Transactions | ||||
| 		deletedLogs []*types.Log | ||||
| 		rebirthLogs []*types.Log | ||||
| 		// collectLogs collects the logs that were generated during the
 | ||||
| 		// processing of the block that corresponds with the given hash.
 | ||||
| 		// These logs are later announced as deleted.
 | ||||
| 		collectLogs = func(hash common.Hash) { | ||||
| 			// Coalesce logs and set 'Removed'.
 | ||||
| 		// These logs are later announced as deleted or reborn
 | ||||
| 		collectLogs = func(hash common.Hash, removed bool) { | ||||
| 			number := bc.hc.GetBlockNumber(hash) | ||||
| 			if number == nil { | ||||
| 				return | ||||
| @ -1487,9 +1487,13 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 			receipts := rawdb.ReadReceipts(bc.db, hash, *number) | ||||
| 			for _, receipt := range receipts { | ||||
| 				for _, log := range receipt.Logs { | ||||
| 					del := *log | ||||
| 					del.Removed = true | ||||
| 					deletedLogs = append(deletedLogs, &del) | ||||
| 					l := *log | ||||
| 					if removed { | ||||
| 						l.Removed = true | ||||
| 						deletedLogs = append(deletedLogs, &l) | ||||
| 					} else { | ||||
| 						rebirthLogs = append(rebirthLogs, &l) | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| @ -1502,7 +1506,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 			oldChain = append(oldChain, oldBlock) | ||||
| 			deletedTxs = append(deletedTxs, oldBlock.Transactions()...) | ||||
| 
 | ||||
| 			collectLogs(oldBlock.Hash()) | ||||
| 			collectLogs(oldBlock.Hash(), true) | ||||
| 		} | ||||
| 	} else { | ||||
| 		// reduce new chain and append new chain blocks for inserting later on
 | ||||
| @ -1526,7 +1530,7 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 		oldChain = append(oldChain, oldBlock) | ||||
| 		newChain = append(newChain, newBlock) | ||||
| 		deletedTxs = append(deletedTxs, oldBlock.Transactions()...) | ||||
| 		collectLogs(oldBlock.Hash()) | ||||
| 		collectLogs(oldBlock.Hash(), true) | ||||
| 
 | ||||
| 		oldBlock, newBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1), bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) | ||||
| 		if oldBlock == nil { | ||||
| @ -1552,6 +1556,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 	for i := len(newChain) - 1; i >= 0; i-- { | ||||
| 		// insert the block in the canonical way, re-writing history
 | ||||
| 		bc.insert(newChain[i]) | ||||
| 		// collect reborn logs due to chain reorg(except head block)
 | ||||
| 		if i != 0 { | ||||
| 			collectLogs(newChain[i].Hash(), false) | ||||
| 		} | ||||
| 		// write lookup entries for hash based transaction/receipt searches
 | ||||
| 		rawdb.WriteTxLookupEntries(bc.db, newChain[i]) | ||||
| 		addedTxs = append(addedTxs, newChain[i].Transactions()...) | ||||
| @ -1569,6 +1577,9 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error { | ||||
| 	if len(deletedLogs) > 0 { | ||||
| 		go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs}) | ||||
| 	} | ||||
| 	if len(rebirthLogs) > 0 { | ||||
| 		go bc.logsFeed.Send(rebirthLogs) | ||||
| 	} | ||||
| 	if len(oldChain) > 0 { | ||||
| 		go func() { | ||||
| 			for _, block := range oldChain { | ||||
|  | ||||
| @ -867,7 +867,6 @@ func TestChainTxReorgs(t *testing.T) { | ||||
| } | ||||
| 
 | ||||
| func TestLogReorgs(t *testing.T) { | ||||
| 
 | ||||
| 	var ( | ||||
| 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||
| 		addr1   = crypto.PubkeyToAddress(key1.PublicKey) | ||||
| @ -913,6 +912,211 @@ func TestLogReorgs(t *testing.T) { | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestLogRebirth(t *testing.T) { | ||||
| 	var ( | ||||
| 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||
| 		addr1   = crypto.PubkeyToAddress(key1.PublicKey) | ||||
| 		db      = ethdb.NewMemDatabase() | ||||
| 		// 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) | ||||
| 	) | ||||
| 
 | ||||
| 	// 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) | ||||
| 
 | ||||
| 	rmLogsCh := make(chan RemovedLogsEvent) | ||||
| 	blockchain.SubscribeRemovedLogsEvent(rmLogsCh) | ||||
| 
 | ||||
| 	chain, _ := 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) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("failed to create tx: %v", err) | ||||
| 			} | ||||
| 			gen.AddTx(tx) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	// Spawn a goroutine to receive log events
 | ||||
| 	go listenNewLog(logsCh, 1) | ||||
| 	if _, err := blockchain.InsertChain(chain); err != nil { | ||||
| 		t.Fatalf("failed to insert chain: %v", err) | ||||
| 	} | ||||
| 	if !<-newLogCh { | ||||
| 		t.Fatalf("failed to receive new log event") | ||||
| 	} | ||||
| 
 | ||||
| 	// Generate long reorg chain
 | ||||
| 	forkChain, _ := 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) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("failed to create tx: %v", err) | ||||
| 			} | ||||
| 			gen.AddTx(tx) | ||||
| 			// Higher block difficulty
 | ||||
| 			gen.OffsetTime(-9) | ||||
| 		} | ||||
| 	}) | ||||
| 
 | ||||
| 	// Spawn a goroutine to receive log events
 | ||||
| 	go listenNewLog(logsCh, 1) | ||||
| 	if _, err := blockchain.InsertChain(forkChain); err != nil { | ||||
| 		t.Fatalf("failed to insert forked chain: %v", err) | ||||
| 	} | ||||
| 	if !<-newLogCh { | ||||
| 		t.Fatalf("failed to receive new log event") | ||||
| 	} | ||||
| 	// 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.") | ||||
| 	} | ||||
| 
 | ||||
| 	newBlocks, _ := GenerateChain(params.TestChainConfig, chain[len(chain)-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") | ||||
| 	} | ||||
| 	// 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.") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestSideLogRebirth(t *testing.T) { | ||||
| 	var ( | ||||
| 		key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291") | ||||
| 		addr1   = crypto.PubkeyToAddress(key1.PublicKey) | ||||
| 		db      = ethdb.NewMemDatabase() | ||||
| 		// 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) | ||||
| 	) | ||||
| 
 | ||||
| 	// 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) | ||||
| 
 | ||||
| 	chain, _ := GenerateChain(params.TestChainConfig, genesis, ethash.NewFaker(), db, 2, func(i int, gen *BlockGen) { | ||||
| 		if i == 1 { | ||||
| 			// Higher block difficulty
 | ||||
| 			gen.OffsetTime(-9) | ||||
| 		} | ||||
| 	}) | ||||
| 	if _, err := blockchain.InsertChain(chain); err != nil { | ||||
| 		t.Fatalf("failed to insert forked chain: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// 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) | ||||
| 			if err != nil { | ||||
| 				t.Fatalf("failed to create tx: %v", err) | ||||
| 			} | ||||
| 			gen.AddTx(tx) | ||||
| 		} | ||||
| 	}) | ||||
| 	if _, err := blockchain.InsertChain(sideChain); err != nil { | ||||
| 		t.Fatalf("failed to insert forked chain: %v", err) | ||||
| 	} | ||||
| 
 | ||||
| 	// 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") | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func TestReorgSideEvent(t *testing.T) { | ||||
| 	var ( | ||||
| 		db      = rawdb.NewMemoryDatabase() | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user