github.com/Oyster-zx/tendermint@v0.34.24-fork/statesync/reactor.go (about) 1 package statesync 2 3 import ( 4 "errors" 5 "sort" 6 "time" 7 8 "github.com/gogo/protobuf/proto" 9 10 abci "github.com/tendermint/tendermint/abci/types" 11 "github.com/tendermint/tendermint/config" 12 tmsync "github.com/tendermint/tendermint/libs/sync" 13 "github.com/tendermint/tendermint/p2p" 14 ssproto "github.com/tendermint/tendermint/proto/tendermint/statesync" 15 "github.com/tendermint/tendermint/proxy" 16 sm "github.com/tendermint/tendermint/state" 17 "github.com/tendermint/tendermint/types" 18 ) 19 20 const ( 21 // SnapshotChannel exchanges snapshot metadata 22 SnapshotChannel = byte(0x60) 23 // ChunkChannel exchanges chunk contents 24 ChunkChannel = byte(0x61) 25 // recentSnapshots is the number of recent snapshots to send and receive per peer. 26 recentSnapshots = 10 27 ) 28 29 // Reactor handles state sync, both restoring snapshots for the local node and serving snapshots 30 // for other nodes. 31 type Reactor struct { 32 p2p.BaseReactor 33 34 cfg config.StateSyncConfig 35 conn proxy.AppConnSnapshot 36 connQuery proxy.AppConnQuery 37 tempDir string 38 39 // This will only be set when a state sync is in progress. It is used to feed received 40 // snapshots and chunks into the sync. 41 mtx tmsync.RWMutex 42 syncer *syncer 43 } 44 45 // NewReactor creates a new state sync reactor. 46 func NewReactor( 47 cfg config.StateSyncConfig, 48 conn proxy.AppConnSnapshot, 49 connQuery proxy.AppConnQuery, 50 tempDir string, 51 ) *Reactor { 52 53 r := &Reactor{ 54 cfg: cfg, 55 conn: conn, 56 connQuery: connQuery, 57 } 58 r.BaseReactor = *p2p.NewBaseReactor("StateSync", r) 59 60 return r 61 } 62 63 // GetChannels implements p2p.Reactor. 64 func (r *Reactor) GetChannels() []*p2p.ChannelDescriptor { 65 return []*p2p.ChannelDescriptor{ 66 { 67 ID: SnapshotChannel, 68 Priority: 5, 69 SendQueueCapacity: 10, 70 RecvMessageCapacity: snapshotMsgSize, 71 MessageType: &ssproto.Message{}, 72 }, 73 { 74 ID: ChunkChannel, 75 Priority: 3, 76 SendQueueCapacity: 10, 77 RecvMessageCapacity: chunkMsgSize, 78 MessageType: &ssproto.Message{}, 79 }, 80 } 81 } 82 83 // OnStart implements p2p.Reactor. 84 func (r *Reactor) OnStart() error { 85 return nil 86 } 87 88 // AddPeer implements p2p.Reactor. 89 func (r *Reactor) AddPeer(peer p2p.Peer) { 90 r.mtx.RLock() 91 defer r.mtx.RUnlock() 92 if r.syncer != nil { 93 r.syncer.AddPeer(peer) 94 } 95 } 96 97 // RemovePeer implements p2p.Reactor. 98 func (r *Reactor) RemovePeer(peer p2p.Peer, reason interface{}) { 99 r.mtx.RLock() 100 defer r.mtx.RUnlock() 101 if r.syncer != nil { 102 r.syncer.RemovePeer(peer) 103 } 104 } 105 106 // Receive implements p2p.Reactor. 107 func (r *Reactor) ReceiveEnvelope(e p2p.Envelope) { 108 if !r.IsRunning() { 109 return 110 } 111 112 err := validateMsg(e.Message) 113 if err != nil { 114 r.Logger.Error("Invalid message", "peer", e.Src, "msg", e.Message, "err", err) 115 r.Switch.StopPeerForError(e.Src, err) 116 return 117 } 118 119 switch e.ChannelID { 120 case SnapshotChannel: 121 switch msg := e.Message.(type) { 122 case *ssproto.SnapshotsRequest: 123 snapshots, err := r.recentSnapshots(recentSnapshots) 124 if err != nil { 125 r.Logger.Error("Failed to fetch snapshots", "err", err) 126 return 127 } 128 for _, snapshot := range snapshots { 129 r.Logger.Debug("Advertising snapshot", "height", snapshot.Height, 130 "format", snapshot.Format, "peer", e.Src.ID()) 131 p2p.SendEnvelopeShim(e.Src, p2p.Envelope{ //nolint: staticcheck 132 ChannelID: e.ChannelID, 133 Message: &ssproto.SnapshotsResponse{ 134 Height: snapshot.Height, 135 Format: snapshot.Format, 136 Chunks: snapshot.Chunks, 137 Hash: snapshot.Hash, 138 Metadata: snapshot.Metadata, 139 }, 140 }, r.Logger) 141 } 142 143 case *ssproto.SnapshotsResponse: 144 r.mtx.RLock() 145 defer r.mtx.RUnlock() 146 if r.syncer == nil { 147 r.Logger.Debug("Received unexpected snapshot, no state sync in progress") 148 return 149 } 150 r.Logger.Debug("Received snapshot", "height", msg.Height, "format", msg.Format, "peer", e.Src.ID()) 151 _, err := r.syncer.AddSnapshot(e.Src, &snapshot{ 152 Height: msg.Height, 153 Format: msg.Format, 154 Chunks: msg.Chunks, 155 Hash: msg.Hash, 156 Metadata: msg.Metadata, 157 }) 158 // TODO: We may want to consider punishing the peer for certain errors 159 if err != nil { 160 r.Logger.Error("Failed to add snapshot", "height", msg.Height, "format", msg.Format, 161 "peer", e.Src.ID(), "err", err) 162 return 163 } 164 165 default: 166 r.Logger.Error("Received unknown message %T", msg) 167 } 168 169 case ChunkChannel: 170 switch msg := e.Message.(type) { 171 case *ssproto.ChunkRequest: 172 r.Logger.Debug("Received chunk request", "height", msg.Height, "format", msg.Format, 173 "chunk", msg.Index, "peer", e.Src.ID()) 174 resp, err := r.conn.LoadSnapshotChunkSync(abci.RequestLoadSnapshotChunk{ 175 Height: msg.Height, 176 Format: msg.Format, 177 Chunk: msg.Index, 178 }) 179 if err != nil { 180 r.Logger.Error("Failed to load chunk", "height", msg.Height, "format", msg.Format, 181 "chunk", msg.Index, "err", err) 182 return 183 } 184 r.Logger.Debug("Sending chunk", "height", msg.Height, "format", msg.Format, 185 "chunk", msg.Index, "peer", e.Src.ID()) 186 p2p.SendEnvelopeShim(e.Src, p2p.Envelope{ //nolint: staticcheck 187 ChannelID: ChunkChannel, 188 Message: &ssproto.ChunkResponse{ 189 Height: msg.Height, 190 Format: msg.Format, 191 Index: msg.Index, 192 Chunk: resp.Chunk, 193 Missing: resp.Chunk == nil, 194 }, 195 }, r.Logger) 196 197 case *ssproto.ChunkResponse: 198 r.mtx.RLock() 199 defer r.mtx.RUnlock() 200 if r.syncer == nil { 201 r.Logger.Debug("Received unexpected chunk, no state sync in progress", "peer", e.Src.ID()) 202 return 203 } 204 r.Logger.Debug("Received chunk, adding to sync", "height", msg.Height, "format", msg.Format, 205 "chunk", msg.Index, "peer", e.Src.ID()) 206 _, err := r.syncer.AddChunk(&chunk{ 207 Height: msg.Height, 208 Format: msg.Format, 209 Index: msg.Index, 210 Chunk: msg.Chunk, 211 Sender: e.Src.ID(), 212 }) 213 if err != nil { 214 r.Logger.Error("Failed to add chunk", "height", msg.Height, "format", msg.Format, 215 "chunk", msg.Index, "err", err) 216 return 217 } 218 219 default: 220 r.Logger.Error("Received unknown message %T", msg) 221 } 222 223 default: 224 r.Logger.Error("Received message on invalid channel %x", e.ChannelID) 225 } 226 } 227 228 func (r *Reactor) Receive(chID byte, peer p2p.Peer, msgBytes []byte) { 229 msg := &ssproto.Message{} 230 err := proto.Unmarshal(msgBytes, msg) 231 if err != nil { 232 panic(err) 233 } 234 um, err := msg.Unwrap() 235 if err != nil { 236 panic(err) 237 } 238 239 r.ReceiveEnvelope(p2p.Envelope{ 240 ChannelID: chID, 241 Src: peer, 242 Message: um, 243 }) 244 } 245 246 // recentSnapshots fetches the n most recent snapshots from the app 247 func (r *Reactor) recentSnapshots(n uint32) ([]*snapshot, error) { 248 resp, err := r.conn.ListSnapshotsSync(abci.RequestListSnapshots{}) 249 if err != nil { 250 return nil, err 251 } 252 sort.Slice(resp.Snapshots, func(i, j int) bool { 253 a := resp.Snapshots[i] 254 b := resp.Snapshots[j] 255 switch { 256 case a.Height > b.Height: 257 return true 258 case a.Height == b.Height && a.Format > b.Format: 259 return true 260 default: 261 return false 262 } 263 }) 264 snapshots := make([]*snapshot, 0, n) 265 for i, s := range resp.Snapshots { 266 if i >= recentSnapshots { 267 break 268 } 269 snapshots = append(snapshots, &snapshot{ 270 Height: s.Height, 271 Format: s.Format, 272 Chunks: s.Chunks, 273 Hash: s.Hash, 274 Metadata: s.Metadata, 275 }) 276 } 277 return snapshots, nil 278 } 279 280 // Sync runs a state sync, returning the new state and last commit at the snapshot height. 281 // The caller must store the state and commit in the state database and block store. 282 func (r *Reactor) Sync(stateProvider StateProvider, discoveryTime time.Duration) (sm.State, *types.Commit, error) { 283 r.mtx.Lock() 284 if r.syncer != nil { 285 r.mtx.Unlock() 286 return sm.State{}, nil, errors.New("a state sync is already in progress") 287 } 288 r.syncer = newSyncer(r.cfg, r.Logger, r.conn, r.connQuery, stateProvider, r.tempDir) 289 r.mtx.Unlock() 290 291 hook := func() { 292 r.Logger.Debug("Requesting snapshots from known peers") 293 // Request snapshots from all currently connected peers 294 295 r.Switch.BroadcastEnvelope(p2p.Envelope{ 296 ChannelID: SnapshotChannel, 297 Message: &ssproto.SnapshotsRequest{}, 298 }) 299 } 300 301 hook() 302 303 state, commit, err := r.syncer.SyncAny(discoveryTime, hook) 304 305 r.mtx.Lock() 306 r.syncer = nil 307 r.mtx.Unlock() 308 return state, commit, err 309 }