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