1- package audit_reporter
1+ package host_reporter
22
33import (
44 "context"
@@ -12,6 +12,7 @@ import (
1212 "github.com/LumeraProtocol/supernode/v2/pkg/logtrace"
1313 "github.com/LumeraProtocol/supernode/v2/pkg/lumera"
1414 "github.com/LumeraProtocol/supernode/v2/pkg/reachability"
15+ statussvc "github.com/LumeraProtocol/supernode/v2/supernode/status"
1516 "github.com/cosmos/cosmos-sdk/crypto/keyring"
1617 "google.golang.org/grpc/codes"
1718 "google.golang.org/grpc/status"
@@ -24,7 +25,7 @@ const (
2425 maxConcurrentTargets = 8
2526)
2627
27- // Service submits one MsgSubmitAuditReport per epoch for the local supernode.
28+ // Service submits one MsgSubmitEpochReport per epoch for the local supernode.
2829// All runtime behavior is driven by on-chain params/queries; there are no local config knobs.
2930type Service struct {
3031 identity string
@@ -35,9 +36,12 @@ type Service struct {
3536
3637 pollInterval time.Duration
3738 dialTimeout time.Duration
39+
40+ metrics * statussvc.MetricsCollector
41+ storagePaths []string
3842}
3943
40- func NewService (identity string , lumeraClient lumera.Client , kr keyring.Keyring , keyName string ) (* Service , error ) {
44+ func NewService (identity string , lumeraClient lumera.Client , kr keyring.Keyring , keyName string , baseDir string ) (* Service , error ) {
4145 identity = strings .TrimSpace (identity )
4246 if identity == "" {
4347 return nil , fmt .Errorf ("identity is empty" )
@@ -66,13 +70,21 @@ func NewService(identity string, lumeraClient lumera.Client, kr keyring.Keyring,
6670 return nil , fmt .Errorf ("identity mismatch: config.identity=%s key(%s)=%s" , identity , keyName , got )
6771 }
6872
73+ storagePaths := []string {}
74+ if baseDir = strings .TrimSpace (baseDir ); baseDir != "" {
75+ // Match legacy disk reporting behavior: measure the volume where the supernode stores its data.
76+ storagePaths = []string {baseDir }
77+ }
78+
6979 return & Service {
7080 identity : identity ,
7181 lumera : lumeraClient ,
7282 keyring : kr ,
7383 keyName : keyName ,
7484 pollInterval : defaultPollInterval ,
7585 dialTimeout : defaultDialTimeout ,
86+ metrics : statussvc .NewMetricsCollector (),
87+ storagePaths : storagePaths ,
7688 }, nil
7789}
7890
@@ -105,7 +117,7 @@ func (s *Service) tick(ctx context.Context) {
105117 }
106118
107119 // Idempotency: if a report exists for this epoch, do nothing.
108- if _ , err := s .lumera .Audit ().GetAuditReport (ctx , epochID , s .identity ); err == nil {
120+ if _ , err := s .lumera .Audit ().GetEpochReport (ctx , epochID , s .identity ); err == nil {
109121 return
110122 } else if status .Code (err ) != codes .NotFound {
111123 return
@@ -116,28 +128,49 @@ func (s *Service) tick(ctx context.Context) {
116128 return
117129 }
118130
119- peerObservations := s .buildPeerObservations (ctx , epochID , assignResp .RequiredOpenPorts , assignResp .TargetSupernodeAccounts )
131+ storageChallengeObservations := s .buildStorageChallengeObservations (ctx , epochID , assignResp .RequiredOpenPorts , assignResp .TargetSupernodeAccounts )
132+
133+ hostReport := audittypes.HostReport {
134+ // Intentionally submit 0% usage for CPU/memory so the chain treats these as "unknown".
135+ // Disk usage is reported accurately (legacy-aligned) so disk-based enforcement can work.
136+ CpuUsagePercent : 0 ,
137+ MemUsagePercent : 0 ,
138+ }
139+ if diskUsagePercent , ok := s .diskUsagePercent (ctx ); ok {
140+ hostReport .DiskUsagePercent = diskUsagePercent
141+ }
120142
121- if _ , err := s .lumera .AuditMsg ().SubmitAuditReport (ctx , epochID , peerObservations ); err != nil {
122- logtrace .Warn (ctx , "audit report submit failed" , logtrace.Fields {
143+ if _ , err := s .lumera .AuditMsg ().SubmitEpochReport (ctx , epochID , hostReport , storageChallengeObservations ); err != nil {
144+ logtrace .Warn (ctx , "epoch report submit failed" , logtrace.Fields {
123145 "epoch_id" : epochID ,
124146 "error" : err .Error (),
125147 })
126148 return
127149 }
128150
129- logtrace .Info (ctx , "audit report submitted" , logtrace.Fields {
130- "epoch_id" : epochID ,
131- "peer_observations_count " : len (peerObservations ),
151+ logtrace .Info (ctx , "epoch report submitted" , logtrace.Fields {
152+ "epoch_id" : epochID ,
153+ "storage_challenge_observations_count " : len (storageChallengeObservations ),
132154 })
133155}
134156
135- func (s * Service ) buildPeerObservations (ctx context.Context , epochID uint64 , requiredOpenPorts []uint32 , targets []string ) []* audittypes.AuditPeerObservation {
157+ func (s * Service ) diskUsagePercent (ctx context.Context ) (float64 , bool ) {
158+ if s .metrics == nil || len (s .storagePaths ) == 0 {
159+ return 0 , false
160+ }
161+ infos := s .metrics .CollectStorageMetrics (ctx , s .storagePaths )
162+ if len (infos ) == 0 {
163+ return 0 , false
164+ }
165+ return infos [0 ].UsagePercent , true
166+ }
167+
168+ func (s * Service ) buildStorageChallengeObservations (ctx context.Context , epochID uint64 , requiredOpenPorts []uint32 , targets []string ) []* audittypes.StorageChallengeObservation {
136169 if len (targets ) == 0 {
137170 return nil
138171 }
139172
140- out := make ([]* audittypes.AuditPeerObservation , len (targets ))
173+ out := make ([]* audittypes.StorageChallengeObservation , len (targets ))
141174
142175 type workItem struct {
143176 index int
@@ -171,8 +204,8 @@ func (s *Service) buildPeerObservations(ctx context.Context, epochID uint64, req
171204 <- done
172205 }
173206
174- // ensure no nil elements (MsgSubmitAuditReport rejects nil observations)
175- final := make ([]* audittypes.AuditPeerObservation , 0 , len (out ))
207+ // ensure no nil elements (MsgSubmitEpochReport rejects nil observations)
208+ final := make ([]* audittypes.StorageChallengeObservation , 0 , len (out ))
176209 for i := range out {
177210 if out [i ] != nil {
178211 final = append (final , out [i ])
@@ -181,15 +214,15 @@ func (s *Service) buildPeerObservations(ctx context.Context, epochID uint64, req
181214 return final
182215}
183216
184- func (s * Service ) observeTarget (ctx context.Context , epochID uint64 , requiredOpenPorts []uint32 , target string ) * audittypes.AuditPeerObservation {
217+ func (s * Service ) observeTarget (ctx context.Context , epochID uint64 , requiredOpenPorts []uint32 , target string ) * audittypes.StorageChallengeObservation {
185218 target = strings .TrimSpace (target )
186219 if target == "" {
187220 return nil
188221 }
189222
190223 host , err := s .targetHost (ctx , target )
191224 if err != nil {
192- logtrace .Warn (ctx , "audit observe target: resolve host failed" , logtrace.Fields {
225+ logtrace .Warn (ctx , "storage challenge observe target: resolve host failed" , logtrace.Fields {
193226 "epoch_id" : epochID ,
194227 "target" : target ,
195228 "error" : err .Error (),
@@ -202,7 +235,7 @@ func (s *Service) observeTarget(ctx context.Context, epochID uint64, requiredOpe
202235 portStates = append (portStates , probeTCP (ctx , host , p , s .dialTimeout ))
203236 }
204237
205- return & audittypes.AuditPeerObservation {
238+ return & audittypes.StorageChallengeObservation {
206239 TargetSupernodeAccount : target ,
207240 PortStates : portStates ,
208241 }
0 commit comments