1use crate::sync::{
6 error::Error,
7 message::{
8 msgid, Context, SnapshotManifestRequest, SnapshotManifestResponse,
9 StateSyncCandidateRequest,
10 },
11 state::{
12 state_sync_candidate::state_sync_candidate_manager::StateSyncCandidateManager,
13 state_sync_chunk::snapshot_chunk_manager::{
14 SnapshotChunkConfig, SnapshotChunkManager,
15 },
16 state_sync_manifest::snapshot_manifest_manager::{
17 RelatedData, SnapshotManifestConfig, SnapshotManifestManager,
18 },
19 storage::{Chunk, ChunkKey, SnapshotSyncCandidate},
20 },
21 synchronization_state::PeerFilter,
22 SynchronizationProtocolHandler,
23};
24use cfx_parameters::consensus_internal::REWARD_EPOCH_COUNT;
25use cfx_storage::Result as StorageResult;
26use cfx_types::H256;
27use network::{node_table::NodeId, NetworkContext};
28use parking_lot::RwLock;
29use primitives::EpochId;
30use std::{
31 collections::HashSet,
32 fmt::{Debug, Formatter},
33 sync::Arc,
34 time::{Duration, Instant},
35};
36
37#[derive(Copy, Clone, PartialEq)]
38pub enum Status {
39 Inactive,
40 RequestingCandidates,
41 StartCandidateSync,
42 DownloadingManifest(Instant),
43 DownloadingChunks(Instant),
44 Completed,
45 Invalid,
46}
47
48impl Default for Status {
49 fn default() -> Self { Status::Inactive }
50}
51
52impl Debug for Status {
53 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
54 let status = match self {
55 Status::Inactive => "inactive".into(),
56 Status::RequestingCandidates => "requesting candidates".into(),
57 Status::StartCandidateSync => {
58 "about to request a candidate state".into()
59 }
60 Status::DownloadingManifest(t) => {
61 format!("downloading manifest ({:?})", t.elapsed())
62 }
63 Status::DownloadingChunks(t) => {
64 format!("downloading chunks ({:?})", t.elapsed())
65 }
66 Status::Completed => "completed".into(),
67 Status::Invalid => "invalid".into(),
68 };
69
70 write!(f, "{}", status)
71 }
72}
73
74struct Inner {
77 status: Status,
78
79 sync_candidate_manager: StateSyncCandidateManager,
80 chunk_manager: Option<SnapshotChunkManager>,
82 manifest_manager: Option<SnapshotManifestManager>,
83
84 related_data: Option<RelatedData>,
85 manifest_attempts: usize,
86}
87
88impl Default for Inner {
89 fn default() -> Self { Self::new() }
90}
91
92impl Inner {
93 fn new() -> Self {
94 Self {
95 sync_candidate_manager: Default::default(),
96 status: Status::Inactive,
97 related_data: None,
98 chunk_manager: None,
99 manifest_manager: None,
100 manifest_attempts: 0,
101 }
102 }
103
104 pub fn start_sync_for_candidate(
105 &mut self, sync_candidate: SnapshotSyncCandidate,
106 active_peers: HashSet<NodeId>, trusted_blame_block: H256,
107 io: &dyn NetworkContext, sync_handler: &SynchronizationProtocolHandler,
108 manifest_config: SnapshotManifestConfig,
109 ) {
110 if let Some(chunk_manager) = &mut self.chunk_manager {
111 if chunk_manager.snapshot_candidate == sync_candidate {
112 self.status = Status::DownloadingChunks(Instant::now());
121 chunk_manager.set_active_peers(active_peers);
122 return;
123 }
124 }
125 info!(
126 "start to sync state, snapshot_to_sync = {:?}, trusted blame block = {:?}",
127 sync_candidate, trusted_blame_block);
128 let manifest_manager = SnapshotManifestManager::new_and_start(
129 sync_candidate,
130 trusted_blame_block,
131 active_peers,
132 manifest_config,
133 io,
134 sync_handler,
135 );
136 self.manifest_manager = Some(manifest_manager);
137 self.status = Status::DownloadingManifest(Instant::now());
138 }
139
140 pub fn start_sync(
141 &mut self, current_era_genesis: EpochId,
142 candidates: Vec<SnapshotSyncCandidate>, io: &dyn NetworkContext,
143 sync_handler: &SynchronizationProtocolHandler,
144 ) {
145 let peers = PeerFilter::new(msgid::STATE_SYNC_CANDIDATE_REQUEST)
146 .select_all(&sync_handler.syn);
147 if peers.is_empty() {
148 return;
149 }
150 self.status = Status::RequestingCandidates;
151 self.sync_candidate_manager.reset(
152 current_era_genesis,
153 candidates.clone(),
154 peers.clone(),
155 );
156 self.request_candidates(io, sync_handler, candidates, peers);
157 }
158
159 fn request_candidates(
161 &self, io: &dyn NetworkContext,
162 sync_handler: &SynchronizationProtocolHandler,
163 candidates: Vec<SnapshotSyncCandidate>, peers: Vec<NodeId>,
164 ) {
165 let request = StateSyncCandidateRequest {
166 request_id: 0,
167 candidates,
168 };
169 for peer in peers {
170 sync_handler.request_manager.request_with_delay(
171 io,
172 Box::new(request.clone()),
173 Some(peer),
174 None,
175 );
176 }
177 }
178}
179
180impl Debug for Inner {
181 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
182 write!(
183 f,
184 "(status = {:?}, pending_peers: {}, manifest: {:?}, chunks: {:?})",
185 self.status,
186 self.sync_candidate_manager.pending_peers().len(),
187 self.manifest_manager,
188 self.chunk_manager,
189 )
190 }
191}
192
193pub struct SnapshotChunkSync {
194 inner: Arc<RwLock<Inner>>,
195 config: StateSyncConfiguration,
196}
197
198impl SnapshotChunkSync {
199 pub fn new(config: StateSyncConfiguration) -> Self {
200 SnapshotChunkSync {
201 inner: Default::default(),
202 config,
203 }
204 }
205
206 pub fn status(&self) -> Status { self.inner.read().status }
207
208 pub fn handle_snapshot_manifest_response(
209 &self, ctx: &Context, response: SnapshotManifestResponse,
210 request: &SnapshotManifestRequest,
211 ) -> Result<(), Error> {
212 let inner = &mut *self.inner.write();
213
214 if !matches!(inner.status, Status::DownloadingManifest(_)) {
216 info!("Snapshot manifest received, but mismatch with current status {:?}", inner.status);
217 return Ok(());
218 };
219 if let Some(manifest_manager) = &mut inner.manifest_manager {
220 let r = manifest_manager
221 .handle_snapshot_manifest_response(ctx, response, request)?;
222 if let Some(related_data) = r {
223 let chunk_manager = SnapshotChunkManager::new_and_start(
227 ctx,
228 manifest_manager.snapshot_candidate.clone(),
229 related_data.snapshot_info.clone(),
230 related_data.parent_snapshot_info.clone(),
231 manifest_manager.chunk_boundaries.clone(),
232 manifest_manager.chunk_boundary_proofs.clone(),
233 manifest_manager.active_peers.clone(),
234 self.config.chunk_config(),
235 related_data
240 .true_state_root_by_blame_info
241 .state_root
242 .delta_root,
243 )?;
244 inner.status = Status::DownloadingChunks(Instant::now());
245 inner.chunk_manager = Some(chunk_manager);
246 inner.related_data = Some(related_data);
247 }
248 debug!("sync state progress: {:?}", *inner);
249 } else {
250 error!("manifest manager is None in status {:?}", inner.status);
251 }
252 if matches!(inner.status, Status::DownloadingChunks(_)) {
253 inner.manifest_manager = None;
254 }
255 Ok(())
256 }
257
258 pub fn handle_snapshot_chunk_response(
259 &self, ctx: &Context, chunk_key: ChunkKey, chunk: Chunk,
260 ) -> StorageResult<()> {
261 let mut inner = self.inner.write();
262
263 if !matches!(inner.status, Status::DownloadingChunks(_)) {
264 info!("Snapshot chunk {:?} received, but mismatch with current status {:?}",
265 chunk_key, inner.status);
266 return Ok(());
267 }
268
269 if let Some(chunk_manager) = &mut inner.chunk_manager {
270 if chunk_manager.add_chunk(ctx, chunk_key, chunk)? {
271 inner.status = Status::Completed;
280 }
281 } else {
282 debug!(
283 "Chunk {:?} received in status {:?}",
284 chunk_key, inner.status
285 );
286 }
287 info!("sync state progress: {:?}", *inner);
288 Ok(())
289 }
290
291 pub fn restore_execution_state(
292 &self, sync_handler: &SynchronizationProtocolHandler,
293 ) {
294 let inner = self.inner.read();
295 let related_data = inner
296 .related_data
297 .as_ref()
298 .expect("Set after receving manifest");
299 let mut deferred_block_hash =
300 related_data.snapshot_info.get_snapshot_epoch_id().clone();
301 for i in related_data.blame_vec_offset
305 ..(related_data.blame_vec_offset + REWARD_EPOCH_COUNT as usize)
306 {
307 info!(
308 "insert_epoch_execution_commitment for block hash {:?}",
309 &deferred_block_hash
310 );
311 sync_handler
312 .graph
313 .data_man
314 .insert_epoch_execution_commitment(
315 deferred_block_hash,
316 related_data.true_state_root_by_blame_info.clone(),
319 related_data.receipt_blame_vec[i],
320 related_data.bloom_blame_vec[i],
321 );
322 let block = sync_handler
323 .graph
324 .data_man
325 .block_header_by_hash(&deferred_block_hash)
326 .unwrap();
327 deferred_block_hash = *block.parent_hash();
328 }
329 for (block_hash, epoch_hash, receipts) in &related_data.epoch_receipts {
330 sync_handler.graph.data_man.insert_block_execution_result(
331 *block_hash,
332 *epoch_hash,
333 receipts.clone(),
334 true, );
336 }
337 }
338
339 pub fn handle_snapshot_candidate_response(
343 &self, peer: &NodeId,
344 supported_candidates: &Vec<SnapshotSyncCandidate>,
345 requested_candidates: &Vec<SnapshotSyncCandidate>,
346 ) {
347 self.inner.write().sync_candidate_manager.on_peer_response(
348 peer,
349 supported_candidates,
350 requested_candidates,
351 )
352 }
353
354 pub fn on_peer_disconnected(&self, peer: &NodeId) {
355 let mut inner = self.inner.write();
356 inner.sync_candidate_manager.on_peer_disconnected(peer);
357 if let Some(manifest_manager) = &mut inner.manifest_manager {
358 manifest_manager.on_peer_disconnected(peer);
359 }
360 if let Some(chunk_manager) = &mut inner.chunk_manager {
361 chunk_manager.on_peer_disconnected(peer);
362 }
363 }
364
365 pub fn update_status(
368 &self, current_era_genesis: EpochId, epoch_to_sync: EpochId,
369 io: &dyn NetworkContext, sync_handler: &SynchronizationProtocolHandler,
370 ) {
371 let mut inner = self.inner.write();
372 if inner.manifest_attempts
373 >= self.config.max_downloading_manifest_attempts
374 {
375 error!(
379 "Exceeded max manifest download attempts ({}); resetting state \
380 sync and restarting candidate discovery. This usually means \
381 peers served unusable manifests or chunks.",
382 self.config.max_downloading_manifest_attempts
383 );
384 inner.manifest_attempts = 0;
385 inner.status = Status::Inactive;
386 inner.manifest_manager = None;
387 inner.chunk_manager = None;
388 inner.related_data = None;
389 }
390
391 debug!("sync state status before updating: {:?}", *inner);
392 self.check_timeout(
393 &mut *inner,
394 &Context {
395 node_id: Default::default(),
397 io,
398 manager: sync_handler,
399 },
400 );
401
402 if inner.sync_candidate_manager.current_era_genesis
409 == current_era_genesis
410 {
411 match inner.status {
412 Status::Completed => return,
413 Status::RequestingCandidates => {
414 if inner.sync_candidate_manager.pending_peers().is_empty() {
415 inner.status = Status::StartCandidateSync;
416 inner.sync_candidate_manager.set_active_candidate();
417 }
418 }
419 Status::DownloadingManifest(_) => {
420 if inner
421 .manifest_manager
422 .as_ref()
423 .expect("always set in DownloadingManifest")
424 .is_inactive()
425 {
426 inner.status = Status::StartCandidateSync;
429 inner.sync_candidate_manager.set_active_candidate();
430 }
431 }
432 Status::DownloadingChunks(_) => {
433 if inner
434 .chunk_manager
435 .as_ref()
436 .expect("always set in DownloadingChunks")
437 .is_inactive()
438 {
439 inner.status = Status::StartCandidateSync;
442 inner.sync_candidate_manager.set_active_candidate();
443 }
444 }
445 _ => {}
446 }
447 if inner.sync_candidate_manager.is_inactive()
448 && inner
449 .chunk_manager
450 .as_ref()
451 .map_or(true, |m| m.is_inactive())
452 && inner
453 .manifest_manager
454 .as_ref()
455 .map_or(true, |m| m.is_inactive())
456 {
457 warn!("current sync candidate becomes inactive: {:?}", inner);
461 inner.status = Status::Inactive;
462 inner.manifest_manager = None;
463 }
464 if inner.status == Status::StartCandidateSync {
466 if let Some((sync_candidate, active_peers)) = inner
467 .sync_candidate_manager
468 .get_active_candidate_and_peers()
469 {
470 match sync_handler
471 .graph
472 .consensus
473 .get_trusted_blame_block_for_snapshot(
474 sync_candidate.get_snapshot_epoch_id(),
475 ) {
476 Some(trusted_blame_block) => {
477 inner.start_sync_for_candidate(
478 sync_candidate,
479 active_peers,
480 trusted_blame_block,
481 io,
482 sync_handler,
483 self.config.manifest_config(),
484 );
485 }
486 None => {
487 error!("failed to start checkpoint sync, the trusted blame block is unavailable, epoch_to_sync={:?}", epoch_to_sync);
488 }
489 }
490 } else {
491 inner.status = Status::Inactive;
492 }
493 }
494 } else {
495 inner.status = Status::Inactive;
496 }
497
498 if inner.status == Status::Inactive {
499 let height = sync_handler
502 .graph
503 .data_man
504 .block_header_by_hash(&epoch_to_sync)
505 .expect("Syncing checkpoint should have available header")
506 .height();
507 let candidates = vec![SnapshotSyncCandidate::FullSync {
508 height,
509 snapshot_epoch_id: epoch_to_sync,
510 }];
511 inner.start_sync(current_era_genesis, candidates, io, sync_handler)
512 }
513 debug!("sync state status after updating: {:?}", *inner);
514 }
515
516 fn check_timeout(&self, inner: &mut Inner, ctx: &Context) {
517 inner
518 .sync_candidate_manager
519 .check_timeout(&self.config.candidate_request_timeout);
520 if let Some(manifest_manager) = &mut inner.manifest_manager {
521 manifest_manager.check_timeout(ctx);
522 }
523 if let Some(chunk_manager) = &mut inner.chunk_manager {
524 if !chunk_manager.check_timeout(ctx) {
525 debug!("reset status to Inactive and redownload manifest");
526 inner.status = Status::Inactive;
527 inner.chunk_manager = None;
528 inner.manifest_attempts += 1;
529 }
530 }
531 }
532}
533
534pub struct StateSyncConfiguration {
535 pub max_downloading_chunks: usize,
536 pub candidate_request_timeout: Duration,
537 pub chunk_request_timeout: Duration,
538 pub manifest_request_timeout: Duration,
539 pub max_downloading_manifest_attempts: usize,
540}
541
542impl StateSyncConfiguration {
543 fn chunk_config(&self) -> SnapshotChunkConfig {
544 SnapshotChunkConfig {
545 max_downloading_chunks: self.max_downloading_chunks,
546 chunk_request_timeout: self.chunk_request_timeout,
547 }
548 }
549
550 fn manifest_config(&self) -> SnapshotManifestConfig {
551 SnapshotManifestConfig {
552 manifest_request_timeout: self.manifest_request_timeout,
553 }
554 }
555}