github.com/ethersphere/bee/v2@v2.2.0/pkg/status/status.go (about) 1 // Copyright 2023 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package status 6 7 import ( 8 "context" 9 "fmt" 10 11 "github.com/ethersphere/bee/v2/pkg/log" 12 "github.com/ethersphere/bee/v2/pkg/p2p" 13 "github.com/ethersphere/bee/v2/pkg/p2p/protobuf" 14 "github.com/ethersphere/bee/v2/pkg/postage" 15 "github.com/ethersphere/bee/v2/pkg/status/internal/pb" 16 "github.com/ethersphere/bee/v2/pkg/swarm" 17 "github.com/ethersphere/bee/v2/pkg/topology" 18 ) 19 20 // loggerName is the tree path name of the logger for this package. 21 const loggerName = "status" 22 23 const ( 24 protocolName = "status" 25 protocolVersion = "1.1.1" 26 streamName = "status" 27 ) 28 29 // Snapshot is the current snapshot of the system. 30 type Snapshot pb.Snapshot 31 32 // SyncReporter defines the interface to report syncing rate. 33 type SyncReporter interface { 34 SyncRate() float64 35 } 36 37 // Reserve defines the reserve storage related information required. 38 type Reserve interface { 39 ReserveSize() int 40 ReserveSizeWithinRadius() uint64 41 StorageRadius() uint8 42 } 43 44 type topologyDriver interface { 45 topology.PeerIterator 46 IsReachable() bool 47 } 48 49 // Service is the status service. 50 type Service struct { 51 logger log.Logger 52 streamer p2p.Streamer 53 topologyDriver topologyDriver 54 55 beeMode string 56 reserve Reserve 57 sync SyncReporter 58 chainState postage.ChainStateGetter 59 } 60 61 // NewService creates a new status service. 62 func NewService( 63 logger log.Logger, 64 streamer p2p.Streamer, 65 topology topologyDriver, 66 beeMode string, 67 chainState postage.ChainStateGetter, 68 reserve Reserve, 69 ) *Service { 70 return &Service{ 71 logger: logger.WithName(loggerName).Register(), 72 streamer: streamer, 73 topologyDriver: topology, 74 beeMode: beeMode, 75 chainState: chainState, 76 reserve: reserve, 77 } 78 } 79 80 // LocalSnapshot returns the current status snapshot of this node. 81 func (s *Service) LocalSnapshot() (*Snapshot, error) { 82 var ( 83 storageRadius uint8 84 syncRate float64 85 reserveSize uint64 86 reserveSizeWithinRadius uint64 87 connectedPeers uint64 88 neighborhoodSize uint64 89 ) 90 91 if s.reserve != nil { 92 storageRadius = s.reserve.StorageRadius() 93 reserveSize = uint64(s.reserve.ReserveSize()) 94 reserveSizeWithinRadius = s.reserve.ReserveSizeWithinRadius() 95 } 96 97 if s.sync != nil { 98 syncRate = s.sync.SyncRate() 99 } 100 101 commitment, err := s.chainState.Commitment() 102 if err != nil { 103 return nil, fmt.Errorf("batchstore commitment: %w", err) 104 } 105 106 err = s.topologyDriver.EachConnectedPeer( 107 func(_ swarm.Address, po uint8) (bool, bool, error) { 108 connectedPeers++ 109 if po >= storageRadius { 110 neighborhoodSize++ 111 } 112 return false, false, nil 113 }, 114 topology.Select{}, 115 ) 116 if err != nil { 117 return nil, fmt.Errorf("iterate connected peers: %w", err) 118 } 119 120 return &Snapshot{ 121 BeeMode: s.beeMode, 122 ReserveSize: reserveSize, 123 ReserveSizeWithinRadius: reserveSizeWithinRadius, 124 PullsyncRate: syncRate, 125 StorageRadius: uint32(storageRadius), 126 ConnectedPeers: connectedPeers, 127 NeighborhoodSize: neighborhoodSize + 1, // include self 128 BatchCommitment: commitment, 129 IsReachable: s.topologyDriver.IsReachable(), 130 LastSyncedBlock: s.chainState.GetChainState().Block, 131 }, nil 132 } 133 134 // PeerSnapshot sends request for status snapshot to the peer. 135 func (s *Service) PeerSnapshot(ctx context.Context, peer swarm.Address) (*Snapshot, error) { 136 stream, err := s.streamer.NewStream(ctx, peer, nil, protocolName, protocolVersion, streamName) 137 if err != nil { 138 return nil, fmt.Errorf("new stream: %w", err) 139 } 140 defer func() { 141 go stream.FullClose() 142 }() 143 144 w, r := protobuf.NewWriterAndReader(stream) 145 146 if err := w.WriteMsgWithContext(ctx, new(pb.Get)); err != nil { 147 return nil, fmt.Errorf("write message failed: %w", err) 148 } 149 150 ss := new(pb.Snapshot) 151 if err := r.ReadMsgWithContext(ctx, ss); err != nil { 152 return nil, fmt.Errorf("read message failed: %w", err) 153 } 154 return (*Snapshot)(ss), nil 155 } 156 157 // Protocol returns the protocol specification. 158 func (s *Service) Protocol() p2p.ProtocolSpec { 159 return p2p.ProtocolSpec{ 160 Name: protocolName, 161 Version: protocolVersion, 162 StreamSpecs: []p2p.StreamSpec{{ 163 Name: streamName, 164 Handler: s.handler, 165 }}, 166 } 167 } 168 169 // handler handles the status stream request/response. 170 func (s *Service) handler(ctx context.Context, _ p2p.Peer, stream p2p.Stream) error { 171 loggerV2 := s.logger.V(2).Register() 172 173 w, r := protobuf.NewWriterAndReader(stream) 174 defer func() { 175 if err := stream.FullClose(); err != nil { 176 loggerV2.Debug("stream full close failed: %v", "error", err) 177 } 178 }() 179 180 var msgGet pb.Get 181 if err := r.ReadMsgWithContext(ctx, &msgGet); err != nil { 182 loggerV2.Debug("read message failed", "error", err) 183 return fmt.Errorf("read message: %w", err) 184 } 185 186 snapshot, err := s.LocalSnapshot() 187 if err != nil { 188 loggerV2.Debug("local snapshot failed", "error", err) 189 return fmt.Errorf("local snapshot: %w", err) 190 } 191 192 if err := w.WriteMsgWithContext(ctx, (*pb.Snapshot)(snapshot)); err != nil { 193 loggerV2.Debug("write message failed", "error", err) 194 return fmt.Errorf("write message: %w", err) 195 } 196 197 return nil 198 } 199 200 func (s *Service) SetSync(sync SyncReporter) { 201 s.sync = sync 202 }