@@ -21,6 +21,7 @@ import (
2121const (
2222 defaultPollInterval = 5 * time .Second
2323 defaultDialTimeout = 2 * time .Second
24+ defaultTickTimeout = 30 * time .Second
2425
2526 maxConcurrentTargets = 8
2627)
@@ -103,52 +104,56 @@ func (s *Service) Run(ctx context.Context) error {
103104}
104105
105106func (s * Service ) tick (ctx context.Context ) {
106- epochResp , err := s .lumera .Audit ().GetCurrentEpoch (ctx )
107+ // Bound each reporting cycle so a slow/hung chain RPC cannot block future ticks indefinitely.
108+ tickCtx , cancel := context .WithTimeout (ctx , defaultTickTimeout )
109+ defer cancel ()
110+
111+ epochResp , err := s .lumera .Audit ().GetCurrentEpoch (tickCtx )
107112 if err != nil || epochResp == nil {
108113 return
109114 }
110115 epochID := epochResp .EpochId
111116 reachability .SetCurrentEpochID (epochID )
112117
113- anchorResp , err := s .lumera .Audit ().GetEpochAnchor (ctx , epochID )
118+ anchorResp , err := s .lumera .Audit ().GetEpochAnchor (tickCtx , epochID )
114119 if err != nil || anchorResp == nil || anchorResp .Anchor .EpochId != epochID {
115120 // Anchor may not be committed yet at the epoch boundary; retry on next tick.
116121 return
117122 }
118123
119124 // Idempotency: if a report exists for this epoch, do nothing.
120- if _ , err := s .lumera .Audit ().GetEpochReport (ctx , epochID , s .identity ); err == nil {
125+ if _ , err := s .lumera .Audit ().GetEpochReport (tickCtx , epochID , s .identity ); err == nil {
121126 return
122127 } else if status .Code (err ) != codes .NotFound {
123128 return
124129 }
125130
126- assignResp , err := s .lumera .Audit ().GetAssignedTargets (ctx , s .identity , epochID )
131+ assignResp , err := s .lumera .Audit ().GetAssignedTargets (tickCtx , s .identity , epochID )
127132 if err != nil || assignResp == nil {
128133 return
129134 }
130135
131- storageChallengeObservations := s .buildStorageChallengeObservations (ctx , epochID , assignResp .RequiredOpenPorts , assignResp .TargetSupernodeAccounts )
136+ storageChallengeObservations := s .buildStorageChallengeObservations (tickCtx , epochID , assignResp .RequiredOpenPorts , assignResp .TargetSupernodeAccounts )
132137
133138 hostReport := audittypes.HostReport {
134139 // Intentionally submit 0% usage for CPU/memory so the chain treats these as "unknown".
135140 // Disk usage is reported accurately (legacy-aligned) so disk-based enforcement can work.
136141 CpuUsagePercent : 0 ,
137142 MemUsagePercent : 0 ,
138143 }
139- if diskUsagePercent , ok := s .diskUsagePercent (ctx ); ok {
144+ if diskUsagePercent , ok := s .diskUsagePercent (tickCtx ); ok {
140145 hostReport .DiskUsagePercent = diskUsagePercent
141146 }
142147
143- if _ , err := s .lumera .AuditMsg ().SubmitEpochReport (ctx , epochID , hostReport , storageChallengeObservations ); err != nil {
144- logtrace .Warn (ctx , "epoch report submit failed" , logtrace.Fields {
148+ if _ , err := s .lumera .AuditMsg ().SubmitEpochReport (tickCtx , epochID , hostReport , storageChallengeObservations ); err != nil {
149+ logtrace .Warn (tickCtx , "epoch report submit failed" , logtrace.Fields {
145150 "epoch_id" : epochID ,
146151 "error" : err .Error (),
147152 })
148153 return
149154 }
150155
151- logtrace .Info (ctx , "epoch report submitted" , logtrace.Fields {
156+ logtrace .Info (tickCtx , "epoch report submitted" , logtrace.Fields {
152157 "epoch_id" : epochID ,
153158 "storage_challenge_observations_count" : len (storageChallengeObservations ),
154159 })
@@ -250,12 +255,23 @@ func (s *Service) targetHost(ctx context.Context, supernodeAccount string) (stri
250255 if raw == "" {
251256 return "" , fmt .Errorf ("empty latest address for %s" , supernodeAccount )
252257 }
258+ return normalizeProbeHost (raw ), nil
259+ }
253260
261+ func normalizeProbeHost (raw string ) string {
254262 // LatestAddress is expected to be an IP/host, but tolerate host:port.
255263 if host , _ , splitErr := net .SplitHostPort (raw ); splitErr == nil && host != "" {
256- return host , nil
264+ return host
265+ }
266+
267+ // Handle bracketed IPv6 literals without a port, e.g. "[2001:db8::1]".
268+ if strings .HasPrefix (raw , "[" ) && strings .HasSuffix (raw , "]" ) {
269+ if unbracketed := strings .TrimPrefix (strings .TrimSuffix (raw , "]" ), "[" ); unbracketed != "" {
270+ return unbracketed
271+ }
257272 }
258- return raw , nil
273+
274+ return raw
259275}
260276
261277func probeTCP (ctx context.Context , host string , port uint32 , timeout time.Duration ) audittypes.PortState {
0 commit comments