@@ -13,14 +13,19 @@ import (
13
13
// the exchange to request the head of the chain from the network.
14
14
var NetworkHeadRequestTimeout = time .Second * 2
15
15
16
- // Head returns the network head or an error. It will try to get the most recent network head or return the current
16
+ // Head returns the most recent network head header.
17
+ //
18
+ // It will try to get the most recent network head or return the current
17
19
// non-expired subjective head as a fallback.
18
20
// If the head has changed, it will update the tail with the new head.
19
21
func (s * Syncer [H ]) Head (ctx context.Context , _ ... header.HeadOption [H ]) (H , error ) {
20
- netHead , err := s .networkHead (ctx )
22
+ netHead , updated , err := s .networkHead (ctx )
21
23
if err != nil {
22
24
return netHead , err
23
25
}
26
+ if ! updated {
27
+ return netHead , nil
28
+ }
24
29
25
30
if _ , err = s .subjectiveTail (ctx , netHead ); err != nil {
26
31
return netHead , fmt .Errorf (
@@ -37,17 +42,20 @@ func (s *Syncer[H]) Head(ctx context.Context, _ ...header.HeadOption[H]) (H, err
37
42
return s .localHead (ctx )
38
43
}
39
44
40
- // networkHead returns subjective head ensuring its recency.
41
- // If the subjective head is not recent, it attempts to request the most recent network head from trusted peers
42
- // assuming that trusted peers are always fully synced.
45
+ // networkHead returns subjective head lazily ensuring its recency.
46
+ //
47
+ // If the subjective head is not recent, it requests a more recent network head. If the request is successful
48
+ // and the new head is more recent than the current subjective head, it sets the new head as the local subjective head
49
+ // and reports true.
50
+ //
43
51
// The request is limited with [NetworkHeadRequestTimeout], otherwise the unrecent subjective header is returned.
44
- func (s * Syncer [H ]) networkHead (ctx context.Context ) (H , error ) {
52
+ func (s * Syncer [H ]) networkHead (ctx context.Context ) (H , bool , error ) {
45
53
sbjHead , initialized , err := s .subjectiveHead (ctx )
46
54
if err != nil {
47
- return sbjHead , err
55
+ return sbjHead , false , err
48
56
}
49
57
if isRecent (sbjHead , s .Params .blockTime , s .Params .recencyThreshold ) || initialized {
50
- return sbjHead , nil
58
+ return sbjHead , initialized , nil
51
59
}
52
60
53
61
s .metrics .outdatedHead (ctx )
@@ -75,7 +83,7 @@ func (s *Syncer[H]) networkHead(ctx context.Context) (H, error) {
75
83
sbjHead .Height (),
76
84
)
77
85
78
- return sbjHead , nil
86
+ return sbjHead , false , nil
79
87
}
80
88
// still check if even the newly requested head is outdated
81
89
if ! isRecent (newHead , s .Params .blockTime , s .Params .recencyThreshold ) {
@@ -86,7 +94,7 @@ func (s *Syncer[H]) networkHead(ctx context.Context) (H, error) {
86
94
87
95
if newHead .Height () <= sbjHead .Height () {
88
96
// nothing new, just return what we have already
89
- return sbjHead , nil
97
+ return sbjHead , false , nil
90
98
}
91
99
// set the new head as subjective, skipping expensive verification
92
100
// as it was already verified by the Exchange.
@@ -97,12 +105,13 @@ func (s *Syncer[H]) networkHead(ctx context.Context) (H, error) {
97
105
"height" ,
98
106
newHead .Height (),
99
107
)
100
- return newHead , nil
108
+ return newHead , true , nil
101
109
}
102
110
103
111
// subjectiveHead returns the highest known non-expired subjective Head.
112
+ //
104
113
// If the current subjective head is expired or does not exist,
105
- // it performs automatic subjective (re) initialization by requesting the most recent head from trusted peers.
114
+ // it lazily performs automatic subjective (re) initialization by requesting the most recent head from trusted peers.
106
115
// Reports true if initialization was performed, false otherwise.
107
116
func (s * Syncer [H ]) subjectiveHead (ctx context.Context ) (H , bool , error ) {
108
117
sbjHead , err := s .localHead (ctx )
@@ -136,7 +145,7 @@ func (s *Syncer[H]) subjectiveHead(ctx context.Context) (H, bool, error) {
136
145
}
137
146
138
147
log .Infow ("subjective initialization finished" , "height" , newHead .Height ())
139
- return newHead , false , nil
148
+ return newHead , true , nil
140
149
}
141
150
142
151
// localHead reports the current highest locally known head.
0 commit comments