Merge pull request #18322 from rjl493456442/reomit-log-events
core: re-emit new log event when logs rebirth
This commit is contained in:
commit
7dd3194710
@ -1465,21 +1465,25 @@ func (bc *BlockChain) insertSidechain(block *types.Block, it *insertIterator) (i
|
|||||||
return 0, nil, nil, nil
|
return 0, nil, nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
|
// reorg takes two blocks, an old chain and a new chain and will reconstruct the
|
||||||
// to be part of the new canonical chain and accumulates potential missing transactions and post an
|
// blocks and inserts them to be part of the new canonical chain and accumulates
|
||||||
// event about them
|
// potential missing transactions and post an event about them.
|
||||||
func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||||
var (
|
var (
|
||||||
newChain types.Blocks
|
newChain types.Blocks
|
||||||
oldChain types.Blocks
|
oldChain types.Blocks
|
||||||
commonBlock *types.Block
|
commonBlock *types.Block
|
||||||
|
|
||||||
deletedTxs types.Transactions
|
deletedTxs types.Transactions
|
||||||
|
addedTxs types.Transactions
|
||||||
|
|
||||||
deletedLogs []*types.Log
|
deletedLogs []*types.Log
|
||||||
|
rebirthLogs []*types.Log
|
||||||
|
|
||||||
// collectLogs collects the logs that were generated during the
|
// collectLogs collects the logs that were generated during the
|
||||||
// processing of the block that corresponds with the given hash.
|
// processing of the block that corresponds with the given hash.
|
||||||
// These logs are later announced as deleted.
|
// These logs are later announced as deleted or reborn
|
||||||
collectLogs = func(hash common.Hash) {
|
collectLogs = func(hash common.Hash, removed bool) {
|
||||||
// Coalesce logs and set 'Removed'.
|
|
||||||
number := bc.hc.GetBlockNumber(hash)
|
number := bc.hc.GetBlockNumber(hash)
|
||||||
if number == nil {
|
if number == nil {
|
||||||
return
|
return
|
||||||
@ -1487,53 +1491,60 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||||||
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
|
receipts := rawdb.ReadReceipts(bc.db, hash, *number)
|
||||||
for _, receipt := range receipts {
|
for _, receipt := range receipts {
|
||||||
for _, log := range receipt.Logs {
|
for _, log := range receipt.Logs {
|
||||||
del := *log
|
l := *log
|
||||||
del.Removed = true
|
if removed {
|
||||||
deletedLogs = append(deletedLogs, &del)
|
l.Removed = true
|
||||||
|
deletedLogs = append(deletedLogs, &l)
|
||||||
|
} else {
|
||||||
|
rebirthLogs = append(rebirthLogs, &l)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
// Reduce the longer chain to the same number as the shorter one
|
||||||
// first reduce whoever is higher bound
|
|
||||||
if oldBlock.NumberU64() > newBlock.NumberU64() {
|
if oldBlock.NumberU64() > newBlock.NumberU64() {
|
||||||
// reduce old chain
|
// Old chain is longer, gather all transactions and logs as deleted ones
|
||||||
for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
|
for ; oldBlock != nil && oldBlock.NumberU64() != newBlock.NumberU64(); oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1) {
|
||||||
oldChain = append(oldChain, oldBlock)
|
oldChain = append(oldChain, oldBlock)
|
||||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
||||||
|
collectLogs(oldBlock.Hash(), true)
|
||||||
collectLogs(oldBlock.Hash())
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// reduce new chain and append new chain blocks for inserting later on
|
// New chain is longer, stash all blocks away for subsequent insertion
|
||||||
for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) {
|
for ; newBlock != nil && newBlock.NumberU64() != oldBlock.NumberU64(); newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1) {
|
||||||
newChain = append(newChain, newBlock)
|
newChain = append(newChain, newBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if oldBlock == nil {
|
if oldBlock == nil {
|
||||||
return fmt.Errorf("Invalid old chain")
|
return fmt.Errorf("invalid old chain")
|
||||||
}
|
}
|
||||||
if newBlock == nil {
|
if newBlock == nil {
|
||||||
return fmt.Errorf("Invalid new chain")
|
return fmt.Errorf("invalid new chain")
|
||||||
}
|
}
|
||||||
|
// Both sides of the reorg are at the same number, reduce both until the common
|
||||||
|
// ancestor is found
|
||||||
for {
|
for {
|
||||||
|
// If the common ancestor was found, bail out
|
||||||
if oldBlock.Hash() == newBlock.Hash() {
|
if oldBlock.Hash() == newBlock.Hash() {
|
||||||
commonBlock = oldBlock
|
commonBlock = oldBlock
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
// Remove an old block as well as stash away a new block
|
||||||
oldChain = append(oldChain, oldBlock)
|
oldChain = append(oldChain, oldBlock)
|
||||||
newChain = append(newChain, newBlock)
|
|
||||||
deletedTxs = append(deletedTxs, oldBlock.Transactions()...)
|
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)
|
newChain = append(newChain, newBlock)
|
||||||
|
|
||||||
|
// Step back with both chains
|
||||||
|
oldBlock = bc.GetBlock(oldBlock.ParentHash(), oldBlock.NumberU64()-1)
|
||||||
if oldBlock == nil {
|
if oldBlock == nil {
|
||||||
return fmt.Errorf("Invalid old chain")
|
return fmt.Errorf("invalid old chain")
|
||||||
}
|
}
|
||||||
|
newBlock = bc.GetBlock(newBlock.ParentHash(), newBlock.NumberU64()-1)
|
||||||
if newBlock == nil {
|
if newBlock == nil {
|
||||||
return fmt.Errorf("Invalid new chain")
|
return fmt.Errorf("invalid new chain")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Ensure the user sees large reorgs
|
// Ensure the user sees large reorgs
|
||||||
@ -1548,35 +1559,46 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
|||||||
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
|
log.Error("Impossible reorg, please file an issue", "oldnum", oldBlock.Number(), "oldhash", oldBlock.Hash(), "newnum", newBlock.Number(), "newhash", newBlock.Hash())
|
||||||
}
|
}
|
||||||
// Insert the new chain, taking care of the proper incremental order
|
// Insert the new chain, taking care of the proper incremental order
|
||||||
var addedTxs types.Transactions
|
|
||||||
for i := len(newChain) - 1; i >= 0; i-- {
|
for i := len(newChain) - 1; i >= 0; i-- {
|
||||||
// insert the block in the canonical way, re-writing history
|
// Insert the block in the canonical way, re-writing history
|
||||||
bc.insert(newChain[i])
|
bc.insert(newChain[i])
|
||||||
// write lookup entries for hash based transaction/receipt searches
|
|
||||||
|
// Collect reborn logs due to chain reorg (except head block (reverse order))
|
||||||
|
if i != 0 {
|
||||||
|
collectLogs(newChain[i].Hash(), false)
|
||||||
|
}
|
||||||
|
// Write lookup entries for hash based transaction/receipt searches
|
||||||
rawdb.WriteTxLookupEntries(bc.db, newChain[i])
|
rawdb.WriteTxLookupEntries(bc.db, newChain[i])
|
||||||
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
addedTxs = append(addedTxs, newChain[i].Transactions()...)
|
||||||
}
|
}
|
||||||
// calculate the difference between deleted and added transactions
|
// When transactions get deleted from the database, the receipts that were
|
||||||
diff := types.TxDifference(deletedTxs, addedTxs)
|
// created in the fork must also be deleted
|
||||||
// When transactions get deleted from the database that means the
|
|
||||||
// receipts that were created in the fork must also be deleted
|
|
||||||
batch := bc.db.NewBatch()
|
batch := bc.db.NewBatch()
|
||||||
for _, tx := range diff {
|
for _, tx := range types.TxDifference(deletedTxs, addedTxs) {
|
||||||
rawdb.DeleteTxLookupEntry(batch, tx.Hash())
|
rawdb.DeleteTxLookupEntry(batch, tx.Hash())
|
||||||
}
|
}
|
||||||
batch.Write()
|
batch.Write()
|
||||||
|
|
||||||
|
// If any logs need to be fired, do it now. In theory we could avoid creating
|
||||||
|
// this goroutine if there are no events to fire, but realistcally that only
|
||||||
|
// ever happens if we're reorging empty blocks, which will only happen on idle
|
||||||
|
// networks where performance is not an issue either way.
|
||||||
|
//
|
||||||
|
// TODO(karalabe): Can we get rid of the goroutine somehow to guarantee correct
|
||||||
|
// event ordering?
|
||||||
|
go func() {
|
||||||
if len(deletedLogs) > 0 {
|
if len(deletedLogs) > 0 {
|
||||||
go bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
|
bc.rmLogsFeed.Send(RemovedLogsEvent{deletedLogs})
|
||||||
|
}
|
||||||
|
if len(rebirthLogs) > 0 {
|
||||||
|
bc.logsFeed.Send(rebirthLogs)
|
||||||
}
|
}
|
||||||
if len(oldChain) > 0 {
|
if len(oldChain) > 0 {
|
||||||
go func() {
|
|
||||||
for _, block := range oldChain {
|
for _, block := range oldChain {
|
||||||
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
|
bc.chainSideFeed.Send(ChainSideEvent{Block: block})
|
||||||
}
|
}
|
||||||
}()
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +32,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/core/vm"
|
"github.com/ethereum/go-ethereum/core/vm"
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
|
"github.com/ethereum/go-ethereum/ethdb/memorydb"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -867,7 +868,6 @@ func TestChainTxReorgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestLogReorgs(t *testing.T) {
|
func TestLogReorgs(t *testing.T) {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
@ -913,6 +913,213 @@ func TestLogReorgs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLogRebirth(t *testing.T) {
|
||||||
|
var (
|
||||||
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
db = memorydb.New()
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
// 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
|
||||||
|
if !<-newLogCh {
|
||||||
|
t.Fatalf("failed to receive new log event")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSideLogRebirth(t *testing.T) {
|
||||||
|
var (
|
||||||
|
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||||
|
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||||
|
db = memorydb.New()
|
||||||
|
|
||||||
|
// 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) {
|
func TestReorgSideEvent(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
db = rawdb.NewMemoryDatabase()
|
db = rawdb.NewMemoryDatabase()
|
||||||
|
Loading…
Reference in New Issue
Block a user