Repository: status-im/status-go
Pull request #6422: chore_: improving timesource
Author: login: gabrielmer
Created: 2025-03-13T12:52:13Z | Merged: 2025-03-14T15:10:04Z

Improving `timesource` to use a more modern approach with `ctx` instead of a `quit` channel, so the `timesource` can be used by the message verifier instead of go-waku's one

Important changes:
- [x] small improvements of `timesource`

Issue: https://github.com/waku-org/nwaku/issues/3076


Diff:
diff --git a/node/status_node_services.go b/node/status_node_services.go
index 88f53477304..a489f4bbd20 100644
--- a/node/status_node_services.go
+++ b/node/status_node_services.go
@@ -1,6 +1,7 @@
 package node
 
 import (
+	"context"
 	"crypto/ecdsa"
 	"database/sql"
 	"encoding/json"
@@ -582,7 +583,10 @@ func (b *StatusNode) timeSource() *timesource.NTPTimeSource {
 		b.timeSourceSrvc = timesource.Default()
 		go func() {
 			defer common.LogOnPanic()
-			b.timeSourceSrvc.Start()
+			err := b.timeSourceSrvc.Start(context.Background())
+			if err != nil {
+				panic("could not obtain timesource: " + err.Error())
+			}
 		}()
 	}
 	return b.timeSourceSrvc
diff --git a/timesource/timesource.go b/timesource/timesource.go
index 780461feec1..c203b100889 100644
--- a/timesource/timesource.go
+++ b/timesource/timesource.go
@@ -2,6 +2,7 @@ package timesource
 
 import (
 	"bytes"
+	"context"
 	"errors"
 	"sort"
 	"sync"
@@ -148,9 +149,8 @@ type NTPTimeSource struct {
 	timeQuery         ntpQuery // for ease of testing
 	now               func() time.Time
 
-	quit chan struct{}
-
 	stateMu sync.Mutex
+	cancel  context.CancelFunc
 	started bool
 
 	timeDataMu    sync.RWMutex
@@ -215,7 +215,7 @@ func (s *NTPTimeSource) updateOffset() error {
 
 // runPeriodically runs periodically the given function based on NTPTimeSource
 // synchronization limits (fastNTPSyncPeriod / slowNTPSyncPeriod)
-func (s *NTPTimeSource) runPeriodically(fn func() error, starWithSlowSyncPeriod bool) {
+func (s *NTPTimeSource) runPeriodically(ctx context.Context, fn func() error, starWithSlowSyncPeriod bool) {
 	if s.started {
 		return
 	}
@@ -224,7 +224,6 @@ func (s *NTPTimeSource) runPeriodically(fn func() error, starWithSlowSyncPeriod
 	if starWithSlowSyncPeriod {
 		period = s.slowNTPSyncPeriod
 	}
-	s.quit = make(chan struct{})
 	go func() {
 		defer common.LogOnPanic()
 		for {
@@ -236,7 +235,7 @@ func (s *NTPTimeSource) runPeriodically(fn func() error, starWithSlowSyncPeriod
 					period = s.fastNTPSyncPeriod
 				}
 
-			case <-s.quit:
+			case <-ctx.Done():
 				return
 			}
 		}
@@ -244,16 +243,17 @@ func (s *NTPTimeSource) runPeriodically(fn func() error, starWithSlowSyncPeriod
 }
 
 // Start initializes the local offset and starts a goroutine that periodically updates the local offset.
-func (s *NTPTimeSource) Start() {
+func (s *NTPTimeSource) Start(ctx context.Context) error {
 	s.stateMu.Lock()
 	defer s.stateMu.Unlock()
 	if s.started {
-		return
+		return nil
 	}
 
 	// Initialize time tracking fields immediately
 	currentTime := s.now()
 	s.lastMonotonic = currentTime
+	ctx, cancel := context.WithCancel(ctx)
 
 	// Attempt to update the offset synchronously so that user can have reliable messages right away
 	err := s.updateOffset()
@@ -263,23 +263,28 @@ func (s *NTPTimeSource) Start() {
 		logutils.ZapLogger().Error("failed to update offset", zap.Error(err))
 	}
 
-	s.runPeriodically(s.updateOffset, err == nil)
+	s.runPeriodically(ctx, s.updateOffset, err == nil)
 
 	s.started = true
+	s.cancel = cancel
+
+	return nil
 }
 
 // Stop goroutine that updates time source.
-func (s *NTPTimeSource) Stop() error {
-	if s.quit == nil {
-		return nil
+func (s *NTPTimeSource) Stop() {
+	if s.cancel == nil {
+		return
 	}
-	close(s.quit)
+	s.cancel()
 	s.started = false
-	return nil
 }
 
 func (s *NTPTimeSource) GetCurrentTime() time.Time {
-	s.Start()
+	err := s.Start(context.Background())
+	if err != nil {
+		panic("could not obtain timesource: " + err.Error())
+	}
 	return s.Now()
 }
 
@@ -289,7 +294,10 @@ func (s *NTPTimeSource) GetCurrentTimeInMillis() uint64 {
 
 func GetCurrentTime() time.Time {
 	ts := Default()
-	ts.Start()
+	err := ts.Start(context.Background())
+	if err != nil {
+		panic("could not obtain timesource: " + err.Error())
+	}
 	return ts.Now()
 }
 
diff --git a/timesource/timesource_test.go b/timesource/timesource_test.go
index 071eec47128..de2daca54de 100644
--- a/timesource/timesource_test.go
+++ b/timesource/timesource_test.go
@@ -1,6 +1,7 @@
 package timesource
 
 import (
+	"context"
 	"errors"
 	"sync"
 	"testing"
@@ -214,7 +215,7 @@ func TestRunningPeriodically(t *testing.T) {
 		// on NTPTimeSource specified periods (fastNTPSyncPeriod & slowNTPSyncPeriod)
 		wg := sync.WaitGroup{}
 		wg.Add(1)
-		source.runPeriodically(func() error {
+		source.runPeriodically(context.TODO(), func() error {
 			mu.Lock()
 			periods = append(periods, time.Since(lastCall))
 			mu.Unlock()
@@ -278,14 +279,12 @@ func TestGetCurrentTimeInMillis(t *testing.T) {
 	// test repeat invoke GetCurrentTimeInMillis
 	n = ts.GetCurrentTimeInMillis()
 	require.Equal(t, expectedTime, n)
-	e := ts.Stop()
-	require.NoError(t, e)
+	ts.Stop()
 
 	// test invoke after stop
 	n = ts.GetCurrentTimeInMillis()
 	require.Equal(t, expectedTime, n)
-	e = ts.Stop()
-	require.NoError(t, e)
+	ts.Stop()
 }
 
 func TestGetCurrentTimeOffline(t *testing.T) {
@@ -329,7 +328,6 @@ func TestSystemTimeChangeDetection(t *testing.T) {
 		fastNTPSyncPeriod: 1 * time.Hour,
 		slowNTPSyncPeriod: 1 * time.Hour,
 		now:               mockTimeNow,
-		quit:              make(chan struct{}),
 	}
 
 	// Set up the timeQuery function to track calls
@@ -447,18 +445,17 @@ func TestTimeTrackingInitialization(t *testing.T) {
 		slowNTPSyncPeriod: 1 * time.Hour,
 		timeQuery:         mockQuery,
 		now:               mockTimeNow,
-		quit:              make(chan struct{}),
 	}
 
 	// Verify that time tracking fields are initially zero
 	assert.True(t, ts.lastMonotonic.IsZero(), "lastMonotonic should be zero before Start()")
 
 	// Start the time source
-	ts.Start()
+	err := ts.Start(context.TODO())
+	assert.NoError(t, err, "Start should not return an error")
 
 	defer func() {
-		err := ts.Stop()
-		assert.NoError(t, err, "Stop should not return an error")
+		ts.Stop()
 	}()
 
 	// Verify that time tracking fields are initialized


Reviews:
user: login: richard-ramos
state: APPROVED
author_association: MEMBER
submitted_at: 2025-03-13T13:50:03Z
user: login: Ivansete-status
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T13:59:38Z
user: login: gabrielmer
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-13T14:11:21Z
user: login: igor-sirotin
body: I think you got into some conflicts with https://github.com/status-im/status-go/pull/6358, please make sure it's all good after rebasing 🙂 
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-14T11:54:44Z
user: login: Ivansete-status
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T13:56:11Z
user: login: gabrielmer
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T14:08:48Z

Review comments:
diff_hunk: @@ -217,23 +219,28 @@ func (s *NTPTimeSource) Start() {
 		logutils.ZapLogger().Error("failed to update offset", zap.Error(err))
 	}
 
-	s.runPeriodically(s.updateOffset, err == nil)
+	s.runPeriodically(ctx, s.updateOffset, err == nil)
 
 	s.started = true
+	s.cancel = cancel
+
+	return nil
 }
 
 // Stop goroutine that updates time source.
-func (s *NTPTimeSource) Stop() error {
-	if s.quit == nil {
-		return nil
+func (s *NTPTimeSource) Stop() {
+	if s.cancel == nil {
+		return
 	}
-	close(s.quit)
+	s.cancel()
 	s.started = false
-	return nil
 }
 
 func (s *NTPTimeSource) GetCurrentTime() time.Time {
-	s.Start()
+	err := s.Start(context.Background())
+	if err != nil {
+		panic("could not obtain timesource")
path: timesource/timesource.go
user: login: Ivansete-status
body: To bring some more detail about the generated error. The syntax is not correct though ;P
```suggestion
		panic("could not obtain timesource in timesource.go GetCurrentTime: ", err)
```
created_at: 2025-03-13T13:55:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -243,7 +250,10 @@ func (s *NTPTimeSource) GetCurrentTimeInMillis() uint64 {
 
 func GetCurrentTime() time.Time {
 	ts := Default()
-	ts.Start()
+	err := ts.Start(context.Background())
+	if err != nil {
+		panic("could not obtain timesource")
path: timesource/timesource.go
user: login: Ivansete-status
body: ```suggestion
		panic("could not obtain timesource in timesource.go GetCurrentTime default: ", err)
```
created_at: 2025-03-13T13:57:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -217,23 +219,28 @@ func (s *NTPTimeSource) Start() {
 		logutils.ZapLogger().Error("failed to update offset", zap.Error(err))
 	}
 
-	s.runPeriodically(s.updateOffset, err == nil)
+	s.runPeriodically(ctx, s.updateOffset, err == nil)
 
 	s.started = true
+	s.cancel = cancel
+
+	return nil
 }
 
 // Stop goroutine that updates time source.
-func (s *NTPTimeSource) Stop() error {
-	if s.quit == nil {
-		return nil
+func (s *NTPTimeSource) Stop() {
+	if s.cancel == nil {
+		return
 	}
-	close(s.quit)
+	s.cancel()
 	s.started = false
-	return nil
 }
 
 func (s *NTPTimeSource) GetCurrentTime() time.Time {
-	s.Start()
+	err := s.Start(context.Background())
+	if err != nil {
+		panic("could not obtain timesource")
path: timesource/timesource.go
user: login: gabrielmer
body: ooh, the panic should for sure already include the file and line number to know where the error is coming from, but let's add the error message 100% :))
created_at: 2025-03-13T14:11:21Z
author_association: CONTRIBUTOR
diff_hunk: @@ -446,18 +445,17 @@ func TestTimeTrackingInitialization(t *testing.T) {
 		slowNTPSyncPeriod: 1 * time.Hour,
 		timeQuery:         mockQuery,
 		now:               mockTimeNow,
-		quit:              make(chan struct{}),
 	}
 
 	// Verify that time tracking fields are initially zero
 	assert.True(t, ts.lastMonotonic.IsZero(), "lastMonotonic should be zero before Start()")
 
 	// Start the time source
-	ts.Start()
+	err := ts.Start(context.TODO())
+	assert.NoError(t, err, "Start should not return an error")
 
 	defer func() {
-		err := ts.Stop()
-		assert.NoError(t, err, "Stop should not return an error")
+		ts.Stop()
path: timesource/timesource_test.go
user: login: Ivansete-status
body: I guess this change considers other change in another PR?
created_at: 2025-03-14T13:56:06Z
author_association: CONTRIBUTOR
diff_hunk: @@ -446,18 +445,17 @@ func TestTimeTrackingInitialization(t *testing.T) {
 		slowNTPSyncPeriod: 1 * time.Hour,
 		timeQuery:         mockQuery,
 		now:               mockTimeNow,
-		quit:              make(chan struct{}),
 	}
 
 	// Verify that time tracking fields are initially zero
 	assert.True(t, ts.lastMonotonic.IsZero(), "lastMonotonic should be zero before Start()")
 
 	// Start the time source
-	ts.Start()
+	err := ts.Start(context.TODO())
+	assert.NoError(t, err, "Start should not return an error")
 
 	defer func() {
-		err := ts.Stop()
-		assert.NoError(t, err, "Stop should not return an error")
+		ts.Stop()
path: timesource/timesource_test.go
user: login: gabrielmer
body: yes, this change came after the rebase, which included a call to `Stop()` that expected an error and with our changes it doesn't return an error
created_at: 2025-03-14T14:08:48Z
author_association: CONTRIBUTOR

PR comments:
user: login: status-im-auto
created_at: 2025-03-13T12:55:21Z
body: 
### Jenkins Builds
<details>
<summary>Click to see older builds (24)</summary>

| :grey_question: | Commit | :hash: | Finished (UTC) | Duration | Platform | Result |
|-|-|-|-|-|-|-|
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/ios/job/PR-6422/1/) | 2025-03-13 12:55:20 | ~2 min | `ios` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-ios-250313-125229-e169f8-pr6422.zip) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/android/job/PR-6422/1/) | 2025-03-13 12:55:45 | ~3 min | `android` | [:package:`aar`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-android-250313-125227-e169f8-pr6422.aar) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/macos/job/aarch64/job/main/job/PR-6422/1/) | 2025-03-13 12:56:27 | ~3 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250313-125231-e169f8-pr6422.zip) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/windows/job/x86_64/job/main/job/PR-6422/1/) | 2025-03-13 12:57:04 | ~4 min | `windows` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-windows-250313-125236-e169f8-pr6422.zip) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/macos/job/x86_64/job/main/job/PR-6422/1/) | 2025-03-13 12:58:35 | ~6 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250313-125233-e169f8-pr6422.zip) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/linux/job/x86_64/job/main/job/PR-6422/1/) | 2025-03-13 12:58:46 | ~6 min | `linux` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-linux-250313-125230-e169f8-pr6422.zip) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/1/) | 2025-03-13 13:13:41 | ~21 min | `tests-rpc` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/1//consoleText) |
| :heavy_check_mark: | e169f830 | [#1](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/1/) | 2025-03-13 13:28:18 | ~35 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/1//consoleText) |
| | | | | | | |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/ios/job/PR-6422/2/) | 2025-03-13 14:13:27 | ~2 min | `ios` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-ios-250313-141042-b97d2c-pr6422.zip) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/android/job/PR-6422/2/) | 2025-03-13 14:14:02 | ~3 min | `android` | [:package:`aar`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-android-250313-141041-b97d2c-pr6422.aar) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/windows/job/x86_64/job/main/job/PR-6422/2/) | 2025-03-13 14:14:05 | ~3 min | `windows` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-windows-250313-141051-b97d2c-pr6422.zip) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/macos/job/aarch64/job/main/job/PR-6422/2/) | 2025-03-13 14:14:38 | ~3 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250313-141045-b97d2c-pr6422.zip) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/macos/job/x86_64/job/main/job/PR-6422/2/) | 2025-03-13 14:16:15 | ~5 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250313-141048-b97d2c-pr6422.zip) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/linux/job/x86_64/job/main/job/PR-6422/2/) | 2025-03-13 14:16:52 | ~6 min | `linux` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-linux-250313-141043-b97d2c-pr6422.zip) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/2/) | 2025-03-13 14:31:41 | ~20 min | `tests-rpc` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/2//consoleText) |
| :heavy_check_mark: | b97d2cc0 | [#2](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/2/) | 2025-03-13 14:46:02 | ~35 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/2//consoleText) |
| | | | | | | |
| :x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/windows/job/x86_64/job/main/job/PR-6422/3/) | 2025-03-14 12:23:24 | ~1 min | `windows` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/windows/job/x86_64/job/main/job/PR-6422/3/consoleText) |
| :x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/ios/job/PR-6422/3/) | 2025-03-14 12:23:32 | ~2 min | `ios` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/ios/job/PR-6422/3/consoleText) |
| :x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/macos/job/aarch64/job/main/job/PR-6422/3/) | 2025-03-14 12:23:32 | ~2 min | `macos` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/macos/job/aarch64/job/main/job/PR-6422/3/consoleText) |
| :x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/android/job/PR-6422/3/) | 2025-03-14 12:23:55 | ~2 min | `android` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/android/job/PR-6422/3/consoleText) |
| :heavy_multiplication_x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/3/) | 2025-03-14 12:24:02 | ~2 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/3//consoleText) |
| :x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/linux/job/x86_64/job/main/job/PR-6422/3/) | 2025-03-14 12:24:49 | ~3 min | `linux` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/linux/job/x86_64/job/main/job/PR-6422/3/consoleText) |
| :x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/macos/job/x86_64/job/main/job/PR-6422/3/) | 2025-03-14 12:25:02 | ~3 min | `macos` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/macos/job/x86_64/job/main/job/PR-6422/3/consoleText) |
| :heavy_multiplication_x: | b7345ba9 | [#3](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/3/) | 2025-03-14 12:39:33 | ~18 min | `tests-rpc` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/3//consoleText) |
</details>

| :grey_question: | Commit | :hash: | Finished (UTC) | Duration | Platform | Result |
|-|-|-|-|-|-|-|
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/ios/job/PR-6422/4/) | 2025-03-14 12:36:21 | ~2 min | `ios` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-ios-250314-123330-0fc4af-pr6422.zip) |
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/android/job/PR-6422/4/) | 2025-03-14 12:36:36 | ~3 min | `android` | [:package:`aar`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-android-250314-123329-0fc4af-pr6422.aar) |
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/windows/job/x86_64/job/main/job/PR-6422/4/) | 2025-03-14 12:36:55 | ~3 min | `windows` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-windows-250314-123338-0fc4af-pr6422.zip) |
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/macos/job/aarch64/job/main/job/PR-6422/4/) | 2025-03-14 12:37:32 | ~3 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250314-123333-0fc4af-pr6422.zip) |
| :heavy_multiplication_x: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/4/) | 2025-03-14 12:37:44 | ~4 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/4//consoleText) |
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/macos/job/x86_64/job/main/job/PR-6422/4/) | 2025-03-14 12:39:00 | ~5 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250314-123334-0fc4af-pr6422.zip) |
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/linux/job/x86_64/job/main/job/PR-6422/4/) | 2025-03-14 12:39:33 | ~6 min | `linux` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-linux-250314-123331-0fc4af-pr6422.zip) |
| :heavy_check_mark: | 0fc4af33 | [#4](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/4/) | 2025-03-14 12:59:44 | ~20 min | `tests-rpc` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/4//consoleText) |
| | | | | | | |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/ios/job/PR-6422/5/) | 2025-03-14 13:20:51 | ~2 min | `ios` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-ios-250314-131809-3c0844-pr6422.zip) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/android/job/PR-6422/5/) | 2025-03-14 13:21:17 | ~3 min | `android` | [:package:`aar`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-android-250314-131807-3c0844-pr6422.aar) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/windows/job/x86_64/job/main/job/PR-6422/5/) | 2025-03-14 13:21:46 | ~3 min | `windows` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-windows-250314-131817-3c0844-pr6422.zip) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/macos/job/aarch64/job/main/job/PR-6422/5/) | 2025-03-14 13:22:09 | ~3 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250314-131811-3c0844-pr6422.zip) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/linux/job/x86_64/job/main/job/PR-6422/5/) | 2025-03-14 13:23:28 | ~5 min | `linux` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-linux-250314-131810-3c0844-pr6422.zip) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/macos/job/x86_64/job/main/job/PR-6422/5/) | 2025-03-14 13:23:36 | ~5 min | `macos` | [:package:`zip`](https://status-im-prs.ams3.cdn.digitaloceanspaces.com/status-go-macos-250314-131813-3c0844-pr6422.zip) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/5/) | 2025-03-14 13:38:16 | ~20 min | `tests-rpc` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests-rpc/job/PR-6422/5//consoleText) |
| :heavy_check_mark: | 3c08445e | [#5](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/5/) | 2025-03-14 13:51:45 | ~33 min | `tests` | [:page_facing_up:`log`](https://ci.status.im/job/status-go/job/prs/job/tests/job/PR-6422/5//consoleText) |

author_association: MEMBER
user: login: codecov[bot]
created_at: 2025-03-13T13:28:56Z
body: ## [Codecov](https://app.codecov.io/gh/status-im/status-go/pull/6422?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) Report
Attention: Patch coverage is `61.90476%` with `8 lines` in your changes missing coverage. Please review.
> Project coverage is 61.04%. Comparing base [(`f4a1e88`)](https://app.codecov.io/gh/status-im/status-go/commit/f4a1e88cd5c644023f1899460e471255b6257b72?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) to head [(`3c08445`)](https://app.codecov.io/gh/status-im/status-go/commit/3c08445e2c0693837cea4c35ed90815e864891a2?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im).
> Report is 3 commits behind head on develop.

| [Files with missing lines](https://app.codecov.io/gh/status-im/status-go/pull/6422?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) | Patch % | Lines |
|---|---|---|
| [timesource/timesource.go](https://app.codecov.io/gh/status-im/status-go/pull/6422?src=pr&el=tree&filepath=timesource%2Ftimesource.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im#diff-dGltZXNvdXJjZS90aW1lc291cmNlLmdv) | 66.66% | [3 Missing and 3 partials :warning: ](https://app.codecov.io/gh/status-im/status-go/pull/6422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) |
| [node/status\_node\_services.go](https://app.codecov.io/gh/status-im/status-go/pull/6422?src=pr&el=tree&filepath=node%2Fstatus_node_services.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im#diff-bm9kZS9zdGF0dXNfbm9kZV9zZXJ2aWNlcy5nbw==) | 33.33% | [1 Missing and 1 partial :warning: ](https://app.codecov.io/gh/status-im/status-go/pull/6422?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) |

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff              @@
##           develop    #6422       +/-   ##
============================================
+ Coverage    23.98%   61.04%   +37.06%     
============================================
  Files          802      843       +41     
  Lines       104740   108209     +3469     
============================================
+ Hits         25125    66060    +40935     
+ Misses       76137    34525    -41612     
- Partials      3478     7624     +4146     
```

| [Flag](https://app.codecov.io/gh/status-im/status-go/pull/6422/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) | Coverage Δ | |
|---|---|---|
| [functional](https://app.codecov.io/gh/status-im/status-go/pull/6422/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) | `23.94% <33.33%> (-0.05%)` | :arrow_down: |
| [unit](https://app.codecov.io/gh/status-im/status-go/pull/6422/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) | `59.27% <61.90%> (?)` | |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im#carryforward-flags-in-the-pull-request-comment) to find out more.

| [Files with missing lines](https://app.codecov.io/gh/status-im/status-go/pull/6422?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im) | Coverage Δ | |
|---|---|---|
| [node/status\_node\_services.go](https://app.codecov.io/gh/status-im/status-go/pull/6422?src=pr&el=tree&filepath=node%2Fstatus_node_services.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im#diff-bm9kZS9zdGF0dXNfbm9kZV9zZXJ2aWNlcy5nbw==) | `79.31% <33.33%> (+5.53%)` | :arrow_up: |
| [timesource/timesource.go](https://app.codecov.io/gh/status-im/status-go/pull/6422?src=pr&el=tree&filepath=timesource%2Ftimesource.go&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im#diff-dGltZXNvdXJjZS90aW1lc291cmNlLmdv) | `92.70% <66.66%> (+40.42%)` | :arrow_up: |

... and [628 files with indirect coverage changes](https://app.codecov.io/gh/status-im/status-go/pull/6422/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=status-im)

</details>
author_association: NONE