Repository: livepeer/go-livepeer
Pull request #2772 (2023-03-16 10:04:37)

build(deps): bump actions/setup-go from 3 to 4
Bumps [actions/setup-go](https://github.com/actions/setup-go) from 3 to 4.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a href="https://github.com/actions/setup-go/releases">actions/setup-go's releases</a>.</em></p>
<blockquote>
<h2>v4.0.0</h2>
<p>In scope of release we enable cache by default. The action won’t throw an error if the cache can’t be restored or saved. The action will throw a warning message but it won’t stop a build process. The cache can be disabled by specifying <code>cache: false</code>.</p>
<pre lang="yaml"><code>steps:
  - uses: actions/checkout@v3
  - uses: actions/setup-go@v4
    with:
      go-version: ‘1.19’
  - run: go run hello.go
</code></pre>
<p>Besides, we introduce such changes as</p>
<ul>
<li><a href="https://redirect.github.com/actions/setup-go/pull/305">Allow to use only GOCACHE for cache</a></li>
<li><a href="https://redirect.github.com/actions/setup-go/pull/315">Bump json5 from 2.2.1 to 2.2.3</a></li>
<li><a href="https://redirect.github.com/actions/setup-go/pull/323">Use proper version for primary key in cache</a></li>
<li><a href="https://redirect.github.com/actions/setup-go/pull/351">Always add Go bin to the PATH</a></li>
<li><a href="https://redirect.github.com/actions/setup-go/pull/350">Add step warning if go-version input is empty</a></li>
</ul>
<h2>Add support for stable and oldstable aliases</h2>
<p>In scope of this release we introduce aliases for the <code>go-version</code> input. The <code>stable</code> alias instals the latest stable version of Go. The <code>oldstable</code> alias installs previous latest minor release (the stable is 1.19.x -&gt; the oldstable is 1.18.x).</p>
<h3>Stable</h3>
<pre lang="yaml"><code>steps:
  - uses: actions/checkout@v3
  - uses: actions/setup-go@v3
    with:
      go-version: 'stable'
  - run: go run hello.go
</code></pre>
<h3>OldStable</h3>
<pre lang="yaml"><code>steps:
  - uses: actions/checkout@v3
  - uses: actions/setup-go@v3
    with:
      go-version: 'oldstable'
  - run: go run hello.go
</code></pre>
<h2>Add support for go.work and pass the token input through on GHES</h2>
<p>In scope of this release we added <a href="https://redirect.github.com/actions/setup-go/pull/283">support for go.work file to pass it in go-version-file input</a>.</p>
<pre lang="yaml"><code>steps:
  - uses: actions/checkout@v3
  - uses: actions/setup-go@v3
&lt;/tr&gt;&lt;/table&gt; 
</code></pre>
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a href="https://github.com/actions/setup-go/commit/4d34df0c2316fe8122ab82dc22947d607c0c91f9"><code>4d34df0</code></a> Update configuration files (<a href="https://redirect.github.com/actions/setup-go/issues/348">#348</a>)</li>
<li><a href="https://github.com/actions/setup-go/commit/fdc0d672a12b8fc05ef4c549c3fd5b0f03ebeb4c"><code>fdc0d67</code></a> Add Go bin if go-version input is empty (<a href="https://redirect.github.com/actions/setup-go/issues/351">#351</a>)</li>
<li><a href="https://github.com/actions/setup-go/commit/ebfdf6ac95bce0e7e743a60aba7adf59b400885b"><code>ebfdf6a</code></a> add warning if go-version is empty (<a href="https://redirect.github.com/actions/setup-go/issues/350">#350</a>)</li>
<li><a href="https://github.com/actions/setup-go/commit/b27d76912e7cb72b7262b5be2ec903b6cfaf358a"><code>b27d769</code></a> fix lockfileVersion (<a href="https://redirect.github.com/actions/setup-go/issues/349">#349</a>)</li>
<li><a href="https://github.com/actions/setup-go/commit/c51a7207680f1bee06f4a751214aab70667f9e25"><code>c51a720</code></a> Enable caching by default with default input (<a href="https://redirect.github.com/actions/setup-go/issues/332">#332</a>)</li>
<li><a href="https://github.com/actions/setup-go/commit/6b848af622919e18822928df24a57044dc6ea9ab"><code>6b848af</code></a> Merge pull request <a href="https://redirect.github.com/actions/setup-go/issues/343">#343</a> from akv-platform/reusable-workflow</li>
<li><a href="https://github.com/actions/setup-go/commit/12741cc20952a981105f1dc8366d69f1d24ed472"><code>12741cc</code></a> Format update-config-files.yml</li>
<li><a href="https://github.com/actions/setup-go/commit/7a77a6aab66a15178e313f5908f5ed9b8500894c"><code>7a77a6a</code></a> Merge branch 'main' into reusable-workflow</li>
<li><a href="https://github.com/actions/setup-go/commit/42a0cc8e1491c14a0e9486c3a895d2124f51af1a"><code>42a0cc8</code></a> Add update-config-files.yml</li>
<li><a href="https://github.com/actions/setup-go/commit/7406d654ad8f517886f6c77135494c2b99355381"><code>7406d65</code></a> Add and configure ESLint and update configuration for Prettier (<a href="https://redirect.github.com/actions/setup-go/issues/341">#341</a>)</li>
<li>Additional commits viewable in <a href="https://github.com/actions/setup-go/compare/v3...v4">compare view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=actions/setup-go&package-manager=github_actions&previous-version=3&new-version=4)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

You can trigger a rebase of this PR by commenting `@dependabot rebase`.

[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)

---

<details>
<summary>Dependabot commands and options</summary>
<br />

You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits that have been made to it
- `@dependabot merge` will merge this PR after your CI passes on it
- `@dependabot squash and merge` will squash and merge this PR after your CI passes on it
- `@dependabot cancel merge` will cancel a previously requested merge and block automerging
- `@dependabot reopen` will reopen this PR if it is closed
- `@dependabot close` will close this PR and stop Dependabot recreating it. You can achieve the same result by closing it manually
- `@dependabot ignore this major version` will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)


</details>> **Note**
> Automatic rebases have been disabled on this pull request as it has been open for over 30 days.



Patch:
diff --git a/.editorconfig b/.editorconfig
index 8babc29f..773c51c0 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -14,6 +14,9 @@ end_of_line = lf
 [Makefile]
 indent_style = tab
 
+[Dockerfile]
+indent_style = tab
+
 # YAML Files
 [*.{yaml,sh}]
 indent_size = 2
diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml
index 5f06e30e..4e0fac70 100644
--- a/.github/workflows/build.yaml
+++ b/.github/workflows/build.yaml
@@ -43,7 +43,7 @@ jobs:
           apt update && apt install -yqq git zip unzip zlib1g-dev zlib1g
 
       - name: Check out code
-        uses: actions/checkout@v3.3.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           # Check https://github.com/livepeer/go-livepeer/pull/1891
@@ -52,7 +52,7 @@ jobs:
 
       - name: Set up go
         id: go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.17
           go-version-file: go.mod
@@ -142,7 +142,7 @@ jobs:
 
     steps:
       - name: Check out code
-        uses: actions/checkout@v3.3.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           # Check https://github.com/livepeer/go-livepeer/pull/1891
@@ -151,7 +151,7 @@ jobs:
 
       - name: Set up go
         id: go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.17
           go-version-file: go.mod
@@ -248,7 +248,7 @@ jobs:
           apt update && apt install -yqq git zip unzip zlib1g-dev zlib1g coreutils binutils libc++-dev libc++abi-dev libtool p7zip-full mediainfo xz-utils
 
       - name: Check out code
-        uses: actions/checkout@v3.3.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           # Check https://github.com/livepeer/go-livepeer/pull/1891
@@ -257,7 +257,7 @@ jobs:
 
       - name: Set up go
         id: go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.17
           go-version-file: go.mod
diff --git a/.github/workflows/docker.yaml b/.github/workflows/docker.yaml
index 95baf36c..27346a49 100644
--- a/.github/workflows/docker.yaml
+++ b/.github/workflows/docker.yaml
@@ -22,7 +22,7 @@ jobs:
     runs-on: ubuntu-20.04
     steps:
       - name: Check out code
-        uses: actions/checkout@v3.3.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           # Check https://github.com/livepeer/go-livepeer/pull/1891
diff --git a/.github/workflows/git.yaml b/.github/workflows/git.yaml
index 281e525c..c69f2638 100644
--- a/.github/workflows/git.yaml
+++ b/.github/workflows/git.yaml
@@ -1,11 +1,18 @@
-name: Git Checks
+name: Git checks
 
-on: [pull_request]
+on:
+  pull_request:
+
+concurrency:
+  group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
+  cancel-in-progress: true
 
 jobs:
   block-fixup:
-    runs-on: ubuntu-18.04
+    runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v3.2.0
-    - name: Block Fixup Commit Merge
-      uses: 13rac1/block-fixup-merge-action@v2.0.0
\ No newline at end of file
+      - name: Check out code
+        uses: actions/checkout@v3.5.2
+
+      - name: Block fixup commit merge
+        uses: 13rac1/block-fixup-merge-action@v2.0.0
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 653a0407..4b50b8f5 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -13,7 +13,7 @@ jobs:
     if: ${{ github.event.workflow_run.conclusion == 'success' }}
     steps:
       - name: checkout
-        uses: actions/checkout@v3.2.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           ref: ${{ github.event.workflow_run.head_branch }}
diff --git a/CHANGELOG_PENDING.md b/CHANGELOG_PENDING.md
index f3f60536..cf7e42a5 100644
--- a/CHANGELOG_PENDING.md
+++ b/CHANGELOG_PENDING.md
@@ -8,6 +8,7 @@
 
 #### General
 - \#2758 Accept only active Os to receive traffic and redeem tickets (@leszko)
+- \#2775 Reduce number of ETH RPC calls during block polling (@leszko)
 
 #### Broadcaster
 
@@ -16,8 +17,9 @@
 #### Transcoder
 
 ### Bug Fixes 🐞
-- \# 2759 Parse keystore address without 0x prefix, fix parse error logging
-- \# 2764 Call session end asynchronously to avoid unnecessary blocking
+- \#2759 Parse keystore address without 0x prefix, fix parse error logging
+- \#2764 Call session end asynchronously to avoid unnecessary blocking (@mjh1)
+- \#2777 Only write session end log message if session exists (@mjh1)
 
 #### CLI
 
diff --git a/cmd/livepeer/starter/starter.go b/cmd/livepeer/starter/starter.go
index a4dedb4a..78bce3a7 100755
--- a/cmd/livepeer/starter/starter.go
+++ b/cmd/livepeer/starter/starter.go
@@ -930,7 +930,7 @@ func StartLivepeer(ctx context.Context, cfg LivepeerConfig) {
 		// Backfill events that the node has missed since its last seen block. This method will block
 		// and the node will not continue setup until it finishes
 		glog.Infof("Backfilling block events (this can take a while)...\n")
-		if err := blockWatcher.BackfillEvents(blockWatchCtx); err != nil {
+		if err := blockWatcher.BackfillEvents(blockWatchCtx, nil); err != nil {
 			glog.Errorf("Failed to backfill events: %v", err)
 			return
 		}
diff --git a/core/orchestrator.go b/core/orchestrator.go
index 1382b110..09421427 100644
--- a/core/orchestrator.go
+++ b/core/orchestrator.go
@@ -660,8 +660,13 @@ func (n *LivepeerNode) transcodeSegmentLoop(logCtx context.Context, md *SegTrans
 
 func (n *LivepeerNode) endTranscodingSession(sessionId string, logCtx context.Context) {
 	// timeout; clean up goroutine here
+	var (
+		exists  bool
+		storage *transcodeConfig
+		sess    *RemoteTranscoder
+	)
 	n.storageMutex.Lock()
-	if storage, exists := n.StorageConfigs[sessionId]; exists {
+	if storage, exists = n.StorageConfigs[sessionId]; exists {
 		storage.OS.EndSession()
 		storage.LocalOS.EndSession()
 		delete(n.StorageConfigs, sessionId)
@@ -671,7 +676,7 @@ func (n *LivepeerNode) endTranscodingSession(sessionId string, logCtx context.Co
 	if n.TranscoderManager != nil {
 		n.TranscoderManager.RTmutex.Lock()
 		// send empty segment to signal transcoder internal session teardown if session exist
-		if sess, exists := n.TranscoderManager.streamSessions[sessionId]; exists {
+		if sess, exists = n.TranscoderManager.streamSessions[sessionId]; exists {
 			segData := &net.SegData{
 				AuthToken: &net.AuthToken{SessionId: sessionId},
 			}
@@ -685,7 +690,7 @@ func (n *LivepeerNode) endTranscodingSession(sessionId string, logCtx context.Co
 	}
 	n.segmentMutex.Lock()
 	mid := ManifestID(sessionId)
-	if _, ok := n.SegmentChans[mid]; ok {
+	if _, exists = n.SegmentChans[mid]; exists {
 		close(n.SegmentChans[mid])
 		delete(n.SegmentChans, mid)
 		if lpmon.Enabled {
@@ -693,6 +698,9 @@ func (n *LivepeerNode) endTranscodingSession(sessionId string, logCtx context.Co
 		}
 	}
 	n.segmentMutex.Unlock()
+	if exists {
+		clog.V(common.DEBUG).Infof(logCtx, "Transcoding session ended by the Broadcaster for sessionID=%v", sessionId)
+	}
 }
 
 func (n *LivepeerNode) serveTranscoder(stream net.Transcoder_RegisterTranscoderServer, capacity int, capabilities *net.Capabilities) {
@@ -980,9 +988,7 @@ func (rtm *RemoteTranscoderManager) selectTranscoder(sessionId string, caps *Cap
 
 // ends transcoding session and releases resources
 func (node *LivepeerNode) EndTranscodingSession(sessionId string) {
-	logCtx := context.TODO()
-	node.endTranscodingSession(sessionId, logCtx)
-	clog.V(common.DEBUG).Infof(logCtx, "Transcoding session ended by the Broadcaster for sessionID=%v", sessionId)
+	node.endTranscodingSession(sessionId, context.TODO())
 }
 
 func (node *RemoteTranscoderManager) EndTranscodingSession(sessionId string) {
diff --git a/eth/blockwatch/block_watcher.go b/eth/blockwatch/block_watcher.go
index bc932776..3f49ab2f 100644
--- a/eth/blockwatch/block_watcher.go
+++ b/eth/blockwatch/block_watcher.go
@@ -20,7 +20,7 @@ import (
 // a hard limit of 10,000 logs returned by a single `eth_getLogs` query by Infura's Ethereum nodes so
 // we need to try and stay below it. Parity, Geth and Alchemy all have much higher limits (if any) on
 // the number of logs returned so Infura is by far the limiting factor.
-var maxBlocksInGetLogsQuery = 60
+var maxBlocksInGetLogsQuery = 1000
 
 // EventType describes the types of events emitted by blockwatch.Watcher. A block can be discovered
 // and added to our representation of the chain. During a block re-org, a block previously stored
@@ -90,8 +90,8 @@ func New(config Config) *Watcher {
 // Note that the latest block is never backfilled here from logs. It will be polled separately in syncToLatestBlock().
 // The reason for that is that we always need to propagate events from the latest block even if it does not contain
 // events which are filtered out during the backfilling process.
-func (w *Watcher) BackfillEvents(ctx context.Context) error {
-	events, err := w.getMissedEventsToBackfill(ctx)
+func (w *Watcher) BackfillEvents(ctx context.Context, chainHead *MiniHeader) error {
+	var events, err = w.getMissedEventsToBackfill(ctx, chainHead)
 	if err != nil {
 		return err
 	}
@@ -153,146 +153,12 @@ func (w *Watcher) syncToLatestBlock(ctx context.Context) error {
 	w.Lock()
 	defer w.Unlock()
 
-	if err := w.BackfillEvents(ctx); err != nil {
-		return err
-	}
-
 	newestHeader, err := w.client.HeaderByNumber(nil)
 	if err != nil {
 		return err
 	}
 
-	lastSeenHeader, err := w.stack.Peek()
-	if err != nil {
-		return err
-	}
-
-	if lastSeenHeader == nil {
-		return w.pollNextBlock()
-	}
-
-	for i := lastSeenHeader.Number; i.Cmp(newestHeader.Number) < 0; i = i.Add(i, big.NewInt(1)) {
-		if err := w.pollNextBlock(); err != nil {
-			return err
-		}
-	}
-	return nil
-}
-
-// pollNextBlock polls for the next block header to be added to the block stack.
-// If there are no blocks on the stack, it fetches the first block at the specified
-// `startBlockDepth` supplied at instantiation.
-func (w *Watcher) pollNextBlock() error {
-	var nextBlockNumber *big.Int
-	latestHeader, err := w.stack.Peek()
-	if err != nil {
-		return err
-	}
-	if latestHeader == nil {
-		if w.startBlockDepth == rpc.LatestBlockNumber {
-			nextBlockNumber = nil // Fetch latest block
-		} else {
-			nextBlockNumber = big.NewInt(int64(w.startBlockDepth))
-		}
-	} else {
-		nextBlockNumber = big.NewInt(0).Add(latestHeader.Number, big.NewInt(1))
-	}
-	nextHeader, err := w.client.HeaderByNumber(nextBlockNumber)
-	if err != nil {
-		if err == ethereum.NotFound {
-			return nil // Noop and wait next polling interval
-		}
-		return err
-	}
-
-	events := []*Event{}
-	events, err = w.buildCanonicalChain(nextHeader, events)
-	// Even if an error occurred, we still want to emit the events gathered since we might have
-	// popped blocks off the Stack and they won't be re-added
-	if len(events) != 0 {
-		w.blockFeed.Send(w.enrichWithL1(events))
-	}
-	if err != nil {
-		return err
-	}
-	return nil
-}
-
-func (w *Watcher) buildCanonicalChain(nextHeader *MiniHeader, events []*Event) ([]*Event, error) {
-	latestHeader, err := w.stack.Peek()
-	if err != nil {
-		return nil, err
-	}
-	// Is the stack empty or is it the next block?
-	if latestHeader == nil || nextHeader.Parent == latestHeader.Hash {
-		nextHeader, err := w.addLogs(nextHeader)
-		if err != nil {
-			// Due to block re-orgs & Ethereum node services load-balancing requests across multiple nodes
-			// a block header might be returned, but when fetching it's logs, an "unknown block" error is
-			// returned. This is expected to happen sometimes, and we simply return the events gathered so
-			// far and pick back up where we left off on the next polling interval.
-			if isUnknownBlockErr(err) {
-				glog.V(5).Infof("missing logs for blockNumber=%v - fetching on next polling interval", nextHeader.Number)
-				return events, nil
-			}
-			return events, err
-		}
-		err = w.stack.Push(nextHeader)
-		if err != nil {
-			return events, err
-		}
-		events = append(events, &Event{
-			Type:        Added,
-			BlockHeader: nextHeader,
-		})
-		return events, nil
-	}
-
-	// Pop latestHeader from the stack. We already have a reference to it.
-	if _, err := w.stack.Pop(); err != nil {
-		return events, err
-	}
-	events = append(events, &Event{
-		Type:        Removed,
-		BlockHeader: latestHeader,
-	})
-
-	nextParentHeader, err := w.client.HeaderByHash(nextHeader.Parent)
-	if err != nil {
-		if err == ethereum.NotFound {
-			glog.V(5).Infof("block header not found blockHash=%v - fetching on next polling interval", nextHeader.Parent.Hex())
-			// Noop and wait next polling interval. We remove the popped blocks
-			// and refetch them on the next polling interval.
-			return events, nil
-		}
-		return events, err
-	}
-	events, err = w.buildCanonicalChain(nextParentHeader, events)
-	if err != nil {
-		return events, err
-	}
-	nextHeader, err = w.addLogs(nextHeader)
-	if err != nil {
-		// Due to block re-orgs & Ethereum node services load-balancing requests across multiple nodes
-		// a block header might be returned, but when fetching it's logs, an "unknown block" error is
-		// returned. This is expected to happen sometimes, and we simply return the events gathered so
-		// far and pick back up where we left off on the next polling interval.
-		if isUnknownBlockErr(err) {
-			glog.V(5).Infof("missing logs for blockNumber=%v - fetching on next polling interval", nextHeader.Number)
-			return events, nil
-		}
-		return events, err
-	}
-	err = w.stack.Push(nextHeader)
-	if err != nil {
-		return events, err
-	}
-	events = append(events, &Event{
-		Type:        Added,
-		BlockHeader: nextHeader,
-	})
-
-	return events, nil
+	return w.BackfillEvents(ctx, newestHeader)
 }
 
 func (w *Watcher) addLogs(header *MiniHeader) (*MiniHeader, error) {
@@ -315,7 +181,7 @@ func (w *Watcher) addLogs(header *MiniHeader) (*MiniHeader, error) {
 // If the stored block is older than the latest block, it batch-fetches the events for missing blocks,
 // re-sets the stored blocks and returns the block events found.
 // Note that the latest block is never backfilled, and will be polled separately during in syncToLatestBlock().
-func (w *Watcher) getMissedEventsToBackfill(ctx context.Context) ([]*Event, error) {
+func (w *Watcher) getMissedEventsToBackfill(ctx context.Context, chainHead *MiniHeader) ([]*Event, error) {
 	events := []*Event{}
 
 	var (
@@ -329,27 +195,38 @@ func (w *Watcher) getMissedEventsToBackfill(ctx context.Context) ([]*Event, erro
 		return events, err
 	}
 
-	latestBlock, err := w.client.HeaderByNumber(nil)
-	if err != nil {
-		return events, err
+	if chainHead == nil {
+		var err error
+		chainHead, err = w.client.HeaderByNumber(nil)
+		if err != nil {
+			return events, err
+		}
 	}
 
-	// Latest block will be polled separately in syncToLatestBlock(), so it's not backfilled.
-	preLatestBlockNum := int(latestBlock.Number.Int64()) - 1
+	latestBlockNum := int(chainHead.Number.Int64())
 
 	if latestRetainedBlock != nil {
 		latestRetainedBlockNum = int(latestRetainedBlock.Number.Int64())
 		// Events for latestRetainedBlock already processed, start at latestRetainedBlock + 1
 		startBlockNum = latestRetainedBlockNum + 1
 	} else {
+		err = w.stack.Push(chainHead)
+		if err != nil {
+			return events, err
+		}
+
+		events = append(events, &Event{
+			Type:        Added,
+			BlockHeader: chainHead,
+		})
 		return events, nil
 	}
 
-	if blocksElapsed = preLatestBlockNum - startBlockNum; blocksElapsed <= 0 {
+	if blocksElapsed = latestBlockNum - startBlockNum; blocksElapsed <= 0 {
 		return events, nil
 	}
 
-	logs, furthestBlockProcessed := w.getLogsInBlockRange(ctx, startBlockNum, preLatestBlockNum)
+	logs, furthestBlockProcessed := w.getLogsInBlockRange(ctx, startBlockNum, latestBlockNum)
 	if furthestBlockProcessed > latestRetainedBlockNum {
 		// If we have processed blocks further then the latestRetainedBlock in the DB, we
 		// want to remove all blocks from the DB and insert the furthestBlockProcessed
@@ -365,7 +242,12 @@ func (w *Watcher) getMissedEventsToBackfill(ctx context.Context) ([]*Event, erro
 			}
 		}
 		// Add furthest block processed into the DB
-		latestHeader, err := w.client.HeaderByNumber(big.NewInt(int64(furthestBlockProcessed)))
+		var latestHeader *MiniHeader
+		if chainHead.Number.Int64() == int64(furthestBlockProcessed) {
+			latestHeader = chainHead
+		} else {
+			latestHeader, err = w.client.HeaderByNumber(big.NewInt(int64(furthestBlockProcessed)))
+		}
 		if err != nil {
 			return events, err
 		}
@@ -374,6 +256,11 @@ func (w *Watcher) getMissedEventsToBackfill(ctx context.Context) ([]*Event, erro
 			return events, err
 		}
 
+		events = append(events, &Event{
+			Type:        Added,
+			BlockHeader: latestHeader,
+		})
+
 		// If no logs found, noop
 		if len(logs) == 0 {
 			return events, nil
diff --git a/go.mod b/go.mod
index 236f7c8b..f833aea9 100644
--- a/go.mod
+++ b/go.mod
@@ -12,7 +12,7 @@ require (
 	github.com/golang/glog v0.0.0-20210429001901-424d2337a529
 	github.com/golang/mock v1.6.0
 	github.com/golang/protobuf v1.5.2
-	github.com/jaypipes/ghw v0.9.0
+	github.com/jaypipes/ghw v0.10.0
 	github.com/jaypipes/pcidb v1.0.0
 	github.com/livepeer/go-tools v0.0.0-20220805063103-76df6beb6506
 	github.com/livepeer/livepeer-data v0.4.11
diff --git a/go.sum b/go.sum
index a9fb4d82..86fc063b 100644
--- a/go.sum
+++ b/go.sum
@@ -622,8 +622,8 @@ github.com/influxdata/usage-client v0.0.0-20160829180054-6d3895376368/go.mod h1:
 github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
 github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus=
 github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
-github.com/jaypipes/ghw v0.9.0 h1:TWF4wNIGtZcgDJaiNcFgby5BR8s2ixcUe0ydxNO2McY=
-github.com/jaypipes/ghw v0.9.0/go.mod h1:dXMo19735vXOjpIBDyDYSp31sB2u4hrtRCMxInqQ64k=
+github.com/jaypipes/ghw v0.10.0 h1:UHu9UX08Py315iPojADFPOkmjTsNzHj4g4adsNKKteY=
+github.com/jaypipes/ghw v0.10.0/go.mod h1:jeJGbkRB2lL3/gxYzNYzEDETV1ZJ56OKr+CSeSEym+g=
 github.com/jaypipes/pcidb v1.0.0 h1:vtZIfkiCUE42oYbJS0TAq9XSfSmcsgo9IdxSm9qzYU8=
 github.com/jaypipes/pcidb v1.0.0/go.mod h1:TnYUvqhPBzCKnH34KrIX22kAeEbDCSRJ9cqLRCuNDfk=
 github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e/go.mod h1:G1CVv03EnqU1wYL2dFwXxW2An0az9JTl/ZsqXQeBlkU=


Test patch:
diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml
index b9387e23..0d72d74d 100644
--- a/.github/workflows/test.yaml
+++ b/.github/workflows/test.yaml
@@ -7,6 +7,8 @@ on:
   push:
     branches:
       - master
+    tags:
+      - "v*"
 
 concurrency:
   group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@@ -27,7 +29,7 @@ jobs:
           apt update && apt install -yqq git zip unzip zlib1g-dev zlib1g
 
       - name: Check out code
-        uses: actions/checkout@v3.3.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           # Check https://github.com/livepeer/go-livepeer/pull/1891
@@ -36,7 +38,7 @@ jobs:
 
       - name: Set up go
         id: go
-        uses: actions/setup-go@v3
+        uses: actions/setup-go@v4
         with:
           go-version: 1.17
           go-version-file: go.mod
@@ -121,7 +123,7 @@ jobs:
 
     steps:
       - name: Check out code
-        uses: actions/checkout@v3.3.0
+        uses: actions/checkout@v3.5.2
         with:
           fetch-depth: 0
           # Check https://github.com/livepeer/go-livepeer/pull/1891
diff --git a/eth/blockwatch/block_watcher_test.go b/eth/blockwatch/block_watcher_test.go
index 47ac1374..12c41d05 100644
--- a/eth/blockwatch/block_watcher_test.go
+++ b/eth/blockwatch/block_watcher_test.go
@@ -41,9 +41,6 @@ func TestWatcher(t *testing.T) {
 	for i := 0; i < fakeClient.NumberOfTimesteps(); i++ {
 		scenarioLabel := fakeClient.GetScenarioLabel()
 
-		err := watcher.pollNextBlock()
-		require.NoError(t, err)
-
 		retainedBlocks, err := watcher.InspectRetainedBlocks()
 		require.NoError(t, err)
 		expectedRetainedBlocks := fakeClient.ExpectedRetainedBlocks()
@@ -204,15 +201,15 @@ func TestGetMissedEventsToBackfillSomeMissed(t *testing.T) {
 
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
-	events, err := watcher.getMissedEventsToBackfill(ctx)
+	events, err := watcher.getMissedEventsToBackfill(ctx, nil)
 	require.NoError(t, err)
-	assert.Len(t, events, 1)
+	assert.Len(t, events, 2)
 
 	// Check that block 30 is now in the DB, and block 5 was removed.
 	headers, err := store.FindAllMiniHeadersSortedByNumber()
 	require.NoError(t, err)
 	require.Len(t, headers, 1)
-	assert.Equal(t, big.NewInt(29), headers[0].Number)
+	assert.Equal(t, big.NewInt(30), headers[0].Number)
 }
 
 func TestGetMissedEventsToBackfillNoneMissed(t *testing.T) {
@@ -236,7 +233,7 @@ func TestGetMissedEventsToBackfillNoneMissed(t *testing.T) {
 
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
-	events, err := watcher.getMissedEventsToBackfill(ctx)
+	events, err := watcher.getMissedEventsToBackfill(ctx, nil)
 	require.NoError(t, err)
 	assert.Len(t, events, 0)
 
@@ -262,13 +259,13 @@ func TestGetMissedEventsToBackfill_NOOP(t *testing.T) {
 
 	ctx, cancel := context.WithCancel(context.Background())
 	defer cancel()
-	events, err := watcher.getMissedEventsToBackfill(ctx)
+	events, err := watcher.getMissedEventsToBackfill(ctx, nil)
 	require.NoError(t, err)
-	assert.Len(t, events, 0)
+	assert.Len(t, events, 1)
 
 	headers, err := store.FindAllMiniHeadersSortedByNumber()
 	require.NoError(t, err)
-	require.Len(t, headers, 0)
+	require.Len(t, headers, 1)
 }
 
 var logStub = types.Log{