mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-23 13:16:42 +00:00
fix future reorged log edge case
Signed-off-by: jsvisa <delweng@gmail.com>
This commit is contained in:
parent
d1f2277b5d
commit
7f30c9e4d1
1 changed files with 42 additions and 26 deletions
|
|
@ -41,6 +41,7 @@ var (
|
||||||
errConnectDropped = errors.New("connection dropped")
|
errConnectDropped = errors.New("connection dropped")
|
||||||
errInvalidToBlock = errors.New("log subscription does not support history block range")
|
errInvalidToBlock = errors.New("log subscription does not support history block range")
|
||||||
errInvalidFromBlock = errors.New("from block can be only a number, or \"safe\", or \"finalized\"")
|
errInvalidFromBlock = errors.New("from block can be only a number, or \"safe\", or \"finalized\"")
|
||||||
|
errClientUnsubscribed = errors.New("client unsubscribed")
|
||||||
)
|
)
|
||||||
|
|
||||||
// filter is a helper struct that holds meta information over the filter type
|
// filter is a helper struct that holds meta information over the filter type
|
||||||
|
|
@ -337,15 +338,17 @@ func (api *FilterAPI) histLogs(notifier notifier, rpcSub *rpc.Subscription, from
|
||||||
liveLogs = make(chan []*types.Log)
|
liveLogs = make(chan []*types.Log)
|
||||||
histLogs = make(chan []*types.Log)
|
histLogs = make(chan []*types.Log)
|
||||||
histDone = make(chan error)
|
histDone = make(chan error)
|
||||||
closeC = make(chan struct{})
|
|
||||||
)
|
)
|
||||||
liveLogsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), liveLogs)
|
liveLogsSub, err := api.events.SubscribeLogs(ethereum.FilterQuery(crit), liveLogs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The original request ctx will be canceled as soon as the parent goroutine
|
||||||
|
// returns a subscription. Use a new context instead.
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
go func() {
|
go func() {
|
||||||
histDone <- api.doHistLogs(from, crit.Addresses, crit.Topics, histLogs, closeC)
|
histDone <- api.doHistLogs(ctx, from, crit.Addresses, crit.Topics, histLogs)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Compose and notify the logs from liveLogs and histLogs
|
// Compose and notify the logs from liveLogs and histLogs
|
||||||
|
|
@ -354,6 +357,7 @@ func (api *FilterAPI) histLogs(notifier notifier, rpcSub *rpc.Subscription, from
|
||||||
delivered uint64
|
delivered uint64
|
||||||
liveOnly bool
|
liveOnly bool
|
||||||
reorged bool
|
reorged bool
|
||||||
|
staleHash common.Hash
|
||||||
)
|
)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
|
@ -381,6 +385,10 @@ func (api *FilterAPI) histLogs(notifier notifier, rpcSub *rpc.Subscription, from
|
||||||
reorged = true
|
reorged = true
|
||||||
}
|
}
|
||||||
if !reorged {
|
if !reorged {
|
||||||
|
// Reorg in future. Remember fork point.
|
||||||
|
if logs[0].Removed && staleHash == (common.Hash{}) {
|
||||||
|
staleHash = logs[0].BlockHash
|
||||||
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if logs[0].Removed {
|
if logs[0].Removed {
|
||||||
|
|
@ -401,18 +409,29 @@ func (api *FilterAPI) histLogs(notifier notifier, rpcSub *rpc.Subscription, from
|
||||||
if reorged {
|
if reorged {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, log := range logs {
|
if logs[0].BlockHash == staleHash {
|
||||||
notifier.Notify(rpcSub.ID, &log)
|
// We have reached the fork point and the historical producer
|
||||||
|
// is emitting old logs because of delay. Restart the process
|
||||||
|
// from last delivered block.
|
||||||
|
logger.Info("Restarting historical logs delivery", "from", logs[0].BlockNumber, "delivered", delivered)
|
||||||
|
liveLogsSub.Unsubscribe()
|
||||||
|
// Stop hist logs fetcher
|
||||||
|
cancel()
|
||||||
|
if err := api.histLogs(notifier, rpcSub, int64(logs[0].BlockNumber), crit); err != nil {
|
||||||
|
logger.Warn("failed to restart historical logs delivery", "err", err)
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
notifyLogsIf(notifier, rpcSub.ID, logs, nil)
|
||||||
// Assuming batch = all logs of a single block
|
// Assuming batch = all logs of a single block
|
||||||
delivered = logs[0].BlockNumber
|
delivered = logs[0].BlockNumber
|
||||||
case <-rpcSub.Err(): // client send an unsubscribe request
|
case <-rpcSub.Err(): // client send an unsubscribe request
|
||||||
liveLogsSub.Unsubscribe()
|
liveLogsSub.Unsubscribe()
|
||||||
closeC <- struct{}{}
|
cancel()
|
||||||
return
|
return
|
||||||
case <-notifier.Closed(): // connection dropped
|
case <-notifier.Closed(): // connection dropped
|
||||||
liveLogsSub.Unsubscribe()
|
liveLogsSub.Unsubscribe()
|
||||||
closeC <- struct{}{}
|
cancel()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -422,29 +441,22 @@ func (api *FilterAPI) histLogs(notifier notifier, rpcSub *rpc.Subscription, from
|
||||||
}
|
}
|
||||||
|
|
||||||
// doHistLogs retrieves the logs older than current header, and forward them to the histLogs channel.
|
// doHistLogs retrieves the logs older than current header, and forward them to the histLogs channel.
|
||||||
func (api *FilterAPI) doHistLogs(from int64, addrs []common.Address, topics [][]common.Hash, histLogs chan<- []*types.Log, closeC chan struct{}) error {
|
func (api *FilterAPI) doHistLogs(ctx context.Context, from int64, addrs []common.Address, topics [][]common.Hash, histLogs chan<- []*types.Log) error {
|
||||||
// The original request ctx will be canceled as soon as the parent goroutine
|
|
||||||
// returns a subscription. Use a new context instead.
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
|
|
||||||
// Fetch logs from a range of blocks.
|
// Fetch logs from a range of blocks.
|
||||||
fetchRange := func(from, to int64) error {
|
fetchRange := func(from, to int64) error {
|
||||||
f := api.sys.NewRangeFilter(from, to, addrs, topics)
|
f := api.sys.NewRangeFilter(from, to, addrs, topics)
|
||||||
logsCh, errChan := f.rangeLogsAsync(ctx)
|
logsCh, errChan := f.rangeLogsAsync(ctx)
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-closeC:
|
|
||||||
cancel()
|
|
||||||
return nil
|
|
||||||
case logs := <-logsCh:
|
case logs := <-logsCh:
|
||||||
histLogs <- logs
|
select {
|
||||||
case err := <-errChan:
|
case histLogs <- logs:
|
||||||
if err != nil {
|
case <-ctx.Done():
|
||||||
logger.Error("Error while fetching historical logs", "err", err)
|
// Flush out all logs until the range filter voluntarily exits.
|
||||||
return err
|
continue
|
||||||
}
|
}
|
||||||
// Range filter is done.
|
case err := <-errChan:
|
||||||
return nil
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -461,6 +473,10 @@ func (api *FilterAPI) doHistLogs(from int64, addrs []common.Address, topics [][]
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if err := fetchRange(from, head); err != nil {
|
if err := fetchRange(from, head); err != nil {
|
||||||
|
if errors.Is(err, context.Canceled) {
|
||||||
|
logger.Info("Historical logs delivery canceled", "from", from, "to", head)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// Move forward to the next batch
|
// Move forward to the next batch
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue