1use crate::LOG_TARGET;
22use sc_transaction_pool_api::ChainEvent;
23use sp_blockchain::TreeRoute;
24use sp_runtime::traits::{Block as BlockT, NumberFor, Saturating};
25use tracing::{debug, trace};
26
27const SKIP_MAINTENANCE_THRESHOLD: u16 = 20;
32
33pub struct EnactmentState<Block>
59where
60 Block: BlockT,
61{
62 recent_best_block: Block::Hash,
63 recent_finalized_block: Block::Hash,
64}
65
66#[derive(Debug)]
68pub enum EnactmentAction<Block: BlockT> {
69 Skip,
71 HandleEnactment(TreeRoute<Block>),
73 HandleFinalization,
75}
76
77impl<Block> EnactmentState<Block>
78where
79 Block: BlockT,
80{
81 pub fn new(recent_best_block: Block::Hash, recent_finalized_block: Block::Hash) -> Self {
83 EnactmentState { recent_best_block, recent_finalized_block }
84 }
85
86 pub fn recent_finalized_block(&self) -> Block::Hash {
88 self.recent_finalized_block
89 }
90
91 pub fn update<TreeRouteF, BlockNumberF>(
95 &mut self,
96 event: &ChainEvent<Block>,
97 tree_route: &TreeRouteF,
98 hash_to_number: &BlockNumberF,
99 ) -> Result<EnactmentAction<Block>, String>
100 where
101 TreeRouteF: Fn(Block::Hash, Block::Hash) -> Result<TreeRoute<Block>, String>,
102 BlockNumberF: Fn(Block::Hash) -> Result<Option<NumberFor<Block>>, String>,
103 {
104 let new_hash = event.hash();
105 let finalized = event.is_finalized();
106
107 let skip_maintenance =
109 match (hash_to_number(new_hash), hash_to_number(self.recent_best_block)) {
110 (Ok(Some(new)), Ok(Some(current))) =>
111 new.saturating_sub(current) > SKIP_MAINTENANCE_THRESHOLD.into(),
112 _ => true,
113 };
114
115 if skip_maintenance {
116 debug!(target: LOG_TARGET, "skip maintain: tree_route would be too long");
117 self.force_update(event);
118 return Ok(EnactmentAction::Skip)
119 }
120
121 if self.recent_finalized_block == new_hash {
123 trace!(target: LOG_TARGET, "handle_enactment: block already finalized");
124 return Ok(EnactmentAction::Skip)
125 }
126
127 let tree_route = tree_route(self.recent_best_block, new_hash)?;
130 trace!(
131 target: LOG_TARGET,
132 ?new_hash,
133 ?finalized,
134 common_block = ?tree_route.common_block(),
135 last_block = ?tree_route.last(),
136 best_block = ?self.recent_best_block,
137 finalized_block = ?self.recent_finalized_block,
138 "resolve hash"
139 );
140
141 if tree_route.retracted().iter().any(|x| x.hash == self.recent_finalized_block) {
145 trace!(
146 target: LOG_TARGET,
147 recent_finalized_block = ?self.recent_finalized_block,
148 ?new_hash,
149 "Recently finalized block would be retracted by ChainEvent, skipping"
150 );
151 return Ok(EnactmentAction::Skip)
152 }
153
154 if finalized {
155 self.recent_finalized_block = new_hash;
156
157 if tree_route.enacted().is_empty() {
162 trace!(target: LOG_TARGET, "handle_enactment: no newly enacted blocks since recent best block");
163 return Ok(EnactmentAction::HandleFinalization)
164 }
165
166 }
168
169 self.recent_best_block = new_hash;
170
171 Ok(EnactmentAction::HandleEnactment(tree_route))
172 }
173
174 pub fn force_update(&mut self, event: &ChainEvent<Block>) {
177 match event {
178 ChainEvent::NewBestBlock { hash, .. } => self.recent_best_block = *hash,
179 ChainEvent::Finalized { hash, .. } => self.recent_finalized_block = *hash,
180 };
181 trace!(
182 target: LOG_TARGET,
183 recent_best_block = ?self.recent_best_block,
184 recent_finalized_block = ?self.recent_finalized_block,
185 "forced update"
186 );
187 }
188}
189
190#[cfg(test)]
191mod enactment_state_tests {
192 use super::{EnactmentAction, EnactmentState};
193 use sc_transaction_pool_api::ChainEvent;
194 use sp_blockchain::{HashAndNumber, TreeRoute};
195 use sp_runtime::traits::NumberFor;
196 use std::sync::Arc;
197 use substrate_test_runtime_client::runtime::{Block, Hash};
198
199 fn a() -> HashAndNumber<Block> {
201 HashAndNumber { number: 1, hash: Hash::from([0xAA; 32]) }
202 }
203 fn b1() -> HashAndNumber<Block> {
204 HashAndNumber { number: 2, hash: Hash::from([0xB1; 32]) }
205 }
206 fn c1() -> HashAndNumber<Block> {
207 HashAndNumber { number: 3, hash: Hash::from([0xC1; 32]) }
208 }
209 fn d1() -> HashAndNumber<Block> {
210 HashAndNumber { number: 4, hash: Hash::from([0xD1; 32]) }
211 }
212 fn e1() -> HashAndNumber<Block> {
213 HashAndNumber { number: 5, hash: Hash::from([0xE1; 32]) }
214 }
215 fn x1() -> HashAndNumber<Block> {
216 HashAndNumber { number: 22, hash: Hash::from([0x1E; 32]) }
217 }
218 fn b2() -> HashAndNumber<Block> {
219 HashAndNumber { number: 2, hash: Hash::from([0xB2; 32]) }
220 }
221 fn c2() -> HashAndNumber<Block> {
222 HashAndNumber { number: 3, hash: Hash::from([0xC2; 32]) }
223 }
224 fn d2() -> HashAndNumber<Block> {
225 HashAndNumber { number: 4, hash: Hash::from([0xD2; 32]) }
226 }
227 fn e2() -> HashAndNumber<Block> {
228 HashAndNumber { number: 5, hash: Hash::from([0xE2; 32]) }
229 }
230 fn x2() -> HashAndNumber<Block> {
231 HashAndNumber { number: 22, hash: Hash::from([0x2E; 32]) }
232 }
233
234 fn test_chain() -> Vec<HashAndNumber<Block>> {
235 vec![x1(), e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2(), x2()]
236 }
237
238 fn block_hash_to_block_number(hash: Hash) -> Result<Option<NumberFor<Block>>, String> {
239 Ok(test_chain().iter().find(|x| x.hash == hash).map(|x| x.number))
240 }
241
242 fn tree_route(from: Hash, to: Hash) -> Result<TreeRoute<Block>, String> {
244 let chain = test_chain();
245 let pivot = chain.iter().position(|x| x.number == a().number).unwrap();
246
247 let from = chain
248 .iter()
249 .position(|bn| bn.hash == from)
250 .ok_or("existing block should be given")?;
251 let to = chain
252 .iter()
253 .position(|bn| bn.hash == to)
254 .ok_or("existing block should be given")?;
255
256 let vec: Vec<HashAndNumber<Block>> = if from < to {
265 chain.into_iter().skip(from).take(to - from + 1).collect()
266 } else {
267 chain.into_iter().skip(to).take(from - to + 1).rev().collect()
268 };
269
270 let pivot = if from <= pivot && to <= pivot {
271 if from < to {
272 to - from
273 } else {
274 0
275 }
276 } else if from >= pivot && to >= pivot {
277 if from < to {
278 0
279 } else {
280 from - to
281 }
282 } else {
283 if from < to {
284 pivot - from
285 } else {
286 from - pivot
287 }
288 };
289
290 TreeRoute::new(vec, pivot)
291 }
292
293 mod mock_tree_route_tests {
294 use super::*;
295
296 fn assert_tree_route_eq(
298 expected: Result<TreeRoute<Block>, String>,
299 result: Result<TreeRoute<Block>, String>,
300 ) {
301 let expected = expected.unwrap();
302 let result = result.unwrap();
303 assert_eq!(result.common_block().hash, expected.common_block().hash);
304 assert_eq!(result.enacted().len(), expected.enacted().len());
305 assert_eq!(result.retracted().len(), expected.retracted().len());
306 assert!(result
307 .enacted()
308 .iter()
309 .zip(expected.enacted().iter())
310 .all(|(a, b)| a.hash == b.hash));
311 assert!(result
312 .retracted()
313 .iter()
314 .zip(expected.retracted().iter())
315 .all(|(a, b)| a.hash == b.hash));
316 }
317
318 #[test]
321 fn tree_route_mock_test_01() {
322 let result = tree_route(b1().hash, a().hash);
323 let expected = TreeRoute::new(vec![b1(), a()], 1);
324 assert_tree_route_eq(result, expected);
325 }
326
327 #[test]
328 fn tree_route_mock_test_02() {
329 let result = tree_route(a().hash, b1().hash);
330 let expected = TreeRoute::new(vec![a(), b1()], 0);
331 assert_tree_route_eq(result, expected);
332 }
333
334 #[test]
335 fn tree_route_mock_test_03() {
336 let result = tree_route(a().hash, c2().hash);
337 let expected = TreeRoute::new(vec![a(), b2(), c2()], 0);
338 assert_tree_route_eq(result, expected);
339 }
340
341 #[test]
342 fn tree_route_mock_test_04() {
343 let result = tree_route(e2().hash, a().hash);
344 let expected = TreeRoute::new(vec![e2(), d2(), c2(), b2(), a()], 4);
345 assert_tree_route_eq(result, expected);
346 }
347
348 #[test]
349 fn tree_route_mock_test_05() {
350 let result = tree_route(d1().hash, b1().hash);
351 let expected = TreeRoute::new(vec![d1(), c1(), b1()], 2);
352 assert_tree_route_eq(result, expected);
353 }
354
355 #[test]
356 fn tree_route_mock_test_06() {
357 let result = tree_route(d2().hash, b2().hash);
358 let expected = TreeRoute::new(vec![d2(), c2(), b2()], 2);
359 assert_tree_route_eq(result, expected);
360 }
361
362 #[test]
363 fn tree_route_mock_test_07() {
364 let result = tree_route(b1().hash, d1().hash);
365 let expected = TreeRoute::new(vec![b1(), c1(), d1()], 0);
366 assert_tree_route_eq(result, expected);
367 }
368
369 #[test]
370 fn tree_route_mock_test_08() {
371 let result = tree_route(b2().hash, d2().hash);
372 let expected = TreeRoute::new(vec![b2(), c2(), d2()], 0);
373 assert_tree_route_eq(result, expected);
374 }
375
376 #[test]
377 fn tree_route_mock_test_09() {
378 let result = tree_route(e2().hash, e1().hash);
379 let expected =
380 TreeRoute::new(vec![e2(), d2(), c2(), b2(), a(), b1(), c1(), d1(), e1()], 4);
381 assert_tree_route_eq(result, expected);
382 }
383
384 #[test]
385 fn tree_route_mock_test_10() {
386 let result = tree_route(e1().hash, e2().hash);
387 let expected =
388 TreeRoute::new(vec![e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2()], 4);
389 assert_tree_route_eq(result, expected);
390 }
391 #[test]
392 fn tree_route_mock_test_11() {
393 let result = tree_route(b1().hash, c2().hash);
394 let expected = TreeRoute::new(vec![b1(), a(), b2(), c2()], 1);
395 assert_tree_route_eq(result, expected);
396 }
397
398 #[test]
399 fn tree_route_mock_test_12() {
400 let result = tree_route(d2().hash, b1().hash);
401 let expected = TreeRoute::new(vec![d2(), c2(), b2(), a(), b1()], 3);
402 assert_tree_route_eq(result, expected);
403 }
404
405 #[test]
406 fn tree_route_mock_test_13() {
407 let result = tree_route(c2().hash, e1().hash);
408 let expected = TreeRoute::new(vec![c2(), b2(), a(), b1(), c1(), d1(), e1()], 2);
409 assert_tree_route_eq(result, expected);
410 }
411
412 #[test]
413 fn tree_route_mock_test_14() {
414 let result = tree_route(b1().hash, b1().hash);
415 let expected = TreeRoute::new(vec![b1()], 0);
416 assert_tree_route_eq(result, expected);
417 }
418
419 #[test]
420 fn tree_route_mock_test_15() {
421 let result = tree_route(b2().hash, b2().hash);
422 let expected = TreeRoute::new(vec![b2()], 0);
423 assert_tree_route_eq(result, expected);
424 }
425
426 #[test]
427 fn tree_route_mock_test_16() {
428 let result = tree_route(a().hash, a().hash);
429 let expected = TreeRoute::new(vec![a()], 0);
430 assert_tree_route_eq(result, expected);
431 }
432
433 #[test]
434 fn tree_route_mock_test_17() {
435 let result = tree_route(x2().hash, b1().hash);
436 let expected = TreeRoute::new(vec![x2(), e2(), d2(), c2(), b2(), a(), b1()], 5);
437 assert_tree_route_eq(result, expected);
438 }
439 }
440
441 fn trigger_new_best_block(
442 state: &mut EnactmentState<Block>,
443 from: HashAndNumber<Block>,
444 acted_on: HashAndNumber<Block>,
445 ) -> EnactmentAction<Block> {
446 let (from, acted_on) = (from.hash, acted_on.hash);
447
448 let event_tree_route = tree_route(from, acted_on).expect("Tree route exists");
449
450 state
451 .update(
452 &ChainEvent::NewBestBlock {
453 hash: acted_on,
454 tree_route: Some(Arc::new(event_tree_route)),
455 },
456 &tree_route,
457 &block_hash_to_block_number,
458 )
459 .unwrap()
460 }
461
462 fn trigger_finalized(
463 state: &mut EnactmentState<Block>,
464 from: HashAndNumber<Block>,
465 acted_on: HashAndNumber<Block>,
466 ) -> EnactmentAction<Block> {
467 let (from, acted_on) = (from.hash, acted_on.hash);
468
469 let v = tree_route(from, acted_on)
470 .expect("Tree route exists")
471 .enacted()
472 .iter()
473 .map(|h| h.hash)
474 .collect::<Vec<_>>();
475
476 state
477 .update(
478 &ChainEvent::Finalized { hash: acted_on, tree_route: v.into() },
479 &tree_route,
480 &block_hash_to_block_number,
481 )
482 .unwrap()
483 }
484
485 fn assert_es_eq(
486 es: &EnactmentState<Block>,
487 expected_best_block: HashAndNumber<Block>,
488 expected_finalized_block: HashAndNumber<Block>,
489 ) {
490 assert_eq!(es.recent_best_block, expected_best_block.hash);
491 assert_eq!(es.recent_finalized_block, expected_finalized_block.hash);
492 }
493
494 #[test]
495 fn test_enactment_helper() {
496 sp_tracing::try_init_simple();
497 let mut es = EnactmentState::new(a().hash, a().hash);
498
499 let result = trigger_new_best_block(&mut es, a(), d1());
506 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
507 assert_es_eq(&es, d1(), a());
508
509 let result = trigger_new_best_block(&mut es, d1(), e1());
510 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
511 assert_es_eq(&es, e1(), a());
512
513 let result = trigger_finalized(&mut es, a(), d2());
514 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
515 assert_es_eq(&es, d2(), d2());
516
517 let result = trigger_new_best_block(&mut es, d2(), e1());
518 assert!(matches!(result, EnactmentAction::Skip));
519 assert_es_eq(&es, d2(), d2());
520
521 let result = trigger_finalized(&mut es, a(), b2());
522 assert!(matches!(result, EnactmentAction::Skip));
523 assert_es_eq(&es, d2(), d2());
524
525 let result = trigger_finalized(&mut es, a(), b1());
526 assert!(matches!(result, EnactmentAction::Skip));
527 assert_es_eq(&es, d2(), d2());
528
529 let result = trigger_new_best_block(&mut es, a(), d2());
530 assert!(matches!(result, EnactmentAction::Skip));
531 assert_es_eq(&es, d2(), d2());
532
533 let result = trigger_finalized(&mut es, a(), d2());
534 assert!(matches!(result, EnactmentAction::Skip));
535 assert_es_eq(&es, d2(), d2());
536
537 let result = trigger_new_best_block(&mut es, a(), c2());
538 assert!(matches!(result, EnactmentAction::Skip));
539 assert_es_eq(&es, d2(), d2());
540
541 let result = trigger_new_best_block(&mut es, a(), c1());
542 assert!(matches!(result, EnactmentAction::Skip));
543 assert_es_eq(&es, d2(), d2());
544
545 let result = trigger_new_best_block(&mut es, d2(), e2());
546 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
547 assert_es_eq(&es, e2(), d2());
548
549 let result = trigger_finalized(&mut es, d2(), e2());
550 assert!(matches!(result, EnactmentAction::HandleFinalization));
551 assert_es_eq(&es, e2(), e2());
552 }
553
554 #[test]
555 fn test_enactment_helper_2() {
556 sp_tracing::try_init_simple();
557 let mut es = EnactmentState::new(a().hash, a().hash);
558
559 let result = trigger_new_best_block(&mut es, a(), b1());
562 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
563 assert_es_eq(&es, b1(), a());
564
565 let result = trigger_new_best_block(&mut es, b1(), c1());
566 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
567 assert_es_eq(&es, c1(), a());
568
569 let result = trigger_new_best_block(&mut es, c1(), d1());
570 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
571 assert_es_eq(&es, d1(), a());
572
573 let result = trigger_new_best_block(&mut es, d1(), e1());
574 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
575 assert_es_eq(&es, e1(), a());
576
577 let result = trigger_finalized(&mut es, a(), c1());
578 assert!(matches!(result, EnactmentAction::HandleFinalization));
579 assert_es_eq(&es, e1(), c1());
580
581 let result = trigger_finalized(&mut es, c1(), e1());
582 assert!(matches!(result, EnactmentAction::HandleFinalization));
583 assert_es_eq(&es, e1(), e1());
584 }
585
586 #[test]
587 fn test_enactment_helper_3() {
588 sp_tracing::try_init_simple();
589 let mut es = EnactmentState::new(a().hash, a().hash);
590
591 let result = trigger_new_best_block(&mut es, a(), e1());
594 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
595 assert_es_eq(&es, e1(), a());
596
597 let result = trigger_finalized(&mut es, a(), b1());
598 assert!(matches!(result, EnactmentAction::HandleFinalization));
599 assert_es_eq(&es, e1(), b1());
600 }
601
602 #[test]
603 fn test_enactment_helper_4() {
604 sp_tracing::try_init_simple();
605 let mut es = EnactmentState::new(a().hash, a().hash);
606
607 let result = trigger_finalized(&mut es, a(), e1());
610 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
611 assert_es_eq(&es, e1(), e1());
612
613 let result = trigger_finalized(&mut es, e1(), b1());
614 assert!(matches!(result, EnactmentAction::Skip));
615 assert_es_eq(&es, e1(), e1());
616 }
617
618 #[test]
619 fn test_enactment_helper_5() {
620 sp_tracing::try_init_simple();
621 let mut es = EnactmentState::new(a().hash, a().hash);
622
623 let result = trigger_finalized(&mut es, a(), e1());
630 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
631 assert_es_eq(&es, e1(), e1());
632
633 let result = trigger_finalized(&mut es, e1(), e2());
634 assert!(matches!(result, EnactmentAction::Skip));
635 assert_es_eq(&es, e1(), e1());
636 }
637
638 #[test]
639 fn test_enactment_helper_6() {
640 sp_tracing::try_init_simple();
641 let mut es = EnactmentState::new(a().hash, a().hash);
642
643 let result = trigger_new_best_block(&mut es, a(), b1());
646 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
647 assert_es_eq(&es, b1(), a());
648
649 let result = trigger_finalized(&mut es, a(), d1());
650 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
651 assert_es_eq(&es, d1(), d1());
652
653 let result = trigger_new_best_block(&mut es, a(), e1());
654 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
655 assert_es_eq(&es, e1(), d1());
656
657 let result = trigger_new_best_block(&mut es, a(), c1());
658 assert!(matches!(result, EnactmentAction::Skip));
659 assert_es_eq(&es, e1(), d1());
660 }
661
662 #[test]
663 fn test_enactment_forced_update_best_block() {
664 sp_tracing::try_init_simple();
665 let mut es = EnactmentState::new(a().hash, a().hash);
666
667 es.force_update(&ChainEvent::NewBestBlock { hash: b1().hash, tree_route: None });
668 assert_es_eq(&es, b1(), a());
669 }
670
671 #[test]
672 fn test_enactment_forced_update_finalize() {
673 sp_tracing::try_init_simple();
674 let mut es = EnactmentState::new(a().hash, a().hash);
675
676 es.force_update(&ChainEvent::Finalized { hash: b1().hash, tree_route: Arc::from([]) });
677 assert_es_eq(&es, a(), b1());
678 }
679
680 #[test]
681 fn test_enactment_skip_long_enacted_path() {
682 sp_tracing::try_init_simple();
683 let mut es = EnactmentState::new(a().hash, a().hash);
684
685 let result = trigger_new_best_block(&mut es, a(), x1());
687 assert!(matches!(result, EnactmentAction::Skip));
688 assert_es_eq(&es, x1(), a());
689 }
690
691 #[test]
692 fn test_enactment_proceed_with_enacted_path_at_threshold() {
693 sp_tracing::try_init_simple();
694 let mut es = EnactmentState::new(b1().hash, b1().hash);
695
696 let result = trigger_new_best_block(&mut es, b1(), x1());
698 assert!(matches!(result, EnactmentAction::HandleEnactment { .. }));
699 assert_es_eq(&es, x1(), b1());
700 }
701}