frame_suite/forks.rs
1// SPDX-License-Identifier: MPL-1.0
2//
3// Part of Auguth Labs open-source softwares.
4// Built for the Substrate framework.
5//
6// This Source Code Form is subject to the terms of the Mozilla Public
7// License, v. 2.0. If a copy of the MPL was not distributed with this
8// file, You can obtain one at https://mozilla.org/MPL/2.0/.
9//
10// Copyright (c) 2026 Auguth Labs (OPC) Pvt Ltd, India
11
12// ===============================================================================
13// ```````````````````````````` FORK-LOCAL UTILITIES `````````````````````````````
14// ===============================================================================
15
16//! Fork-local utilities for pallet offchain workers (OCWs).
17//!
18//! This module does not provide canonical chain selection or consensus.
19//!
20//! Instead, it runs on top of a pallet's OCW execution and maintains a
21//! deterministic local fork graph so the pallet can answer:
22//!
23//! ```ignore
24//! "what was my local state on this branch?"
25//! ```
26//!
27//! A [`Branch`] represents a continuous stream of blocks extending on top
28//! of each other along the same path.
29//!
30//! ```text
31//! A -> B -> C -> D
32//! same branch
33//! ```
34//!
35//! As long as blocks continue as direct children, the same branch is reused
36//! and only the branch head moves forward.
37//!
38//! When a division occurs (a sibling block appears at the same ancestry),
39//! a new fork branch is created.
40//!
41//! ```text
42//! A -> B -> C
43//! |-- D
44//! |-- D'
45//! ```
46//!
47//! Here:
48//!
49//! - `D` is the original branch continuation
50//! - `D'` is the new sibling branch
51//! - `A -> B -> C` is the parent branch of both paths
52//!
53//! Each branch carries its own fork-local scope through [`ForkScopes`].
54//!
55//! When a new sibling branch is created, it inherits from the parent branch
56//! using [`Accrete`]:
57//!
58//! ```text
59//! scope(D') = scope(C).accrete()
60//! ```
61//!
62//! This allows each fork path to maintain isolated local state while still
63//! preserving inherited lineage state.
64//!
65//! ## Usage
66//!
67//! Every pallet using this system should begin its OCW execution with
68//! [`ForksHandler::start`]:
69//!
70//! ```ignore
71//! fn offchain_worker(block_number: BlockNumberFor<T>) {
72//! <Self as ForksHandler<T, MyForkScope>>::start(
73//! Some("my-pallet"),
74//! Some(LogFormatter::default()),
75//! || {
76//! // pallet-specific OCW logic here
77//! }
78//! );
79//! }
80//! ```
81//!
82//! [`ForksHandler::start`] handles:
83//!
84//! - longest-chain extension vs sibling fork creation
85//! - missing branch recovery (during client inactivity)
86//! - safe branch resolution before OCW logic executes
87//!
88//! The OCW closure (main logic) runs only after branch state is
89//! valid and ready to map the fork graph.
90//!
91//! ## Navigation
92//!
93//! Since fork resolution is delayed by one block:
94//!
95//! ```ignore
96//! block N executes
97//! -> block N - 1 is resolved and persisted
98//! ```
99//!
100//! the current executing block is not yet inserted into the local fork graph.
101//!
102//! For normal OCW access, use [`ForksHandler::get_prev_block_branch`]
103//! to retrieve the previous block's (`N - 1`) resolved branch.
104//!
105//! From that branch, navigation can continue using [`ForkAction`] and
106//! [`ForksHandler::transition`], or helpers like:
107//!
108//! - [`ForksHandler::get_block_branch`]
109//! - [`ForksHandler::get_prev_branch`]
110//! - [`ForksHandler::get_branch`]
111//!
112//! This allows movement across:
113//!
114//! - parent branches
115//! - child branches
116//! - sibling branches
117//! - root ancestry
118//!
119//! This provides deterministic branch-local traversal without relying on
120//! canonical consensus routing.
121//!
122//! The system is intentionally fork-aware, scope-first, and best-effort:
123//! it tracks local execution branches, not global consensus finality.
124
125// ===============================================================================
126// ``````````````````````````````````` IMPORTS ```````````````````````````````````
127// ===============================================================================
128
129// --- Local-Crate ---
130use crate::{Accrete, LogFormatter, Logging, Portable};
131
132// --- Scale-codec ---
133use codec::{Decode, Encode};
134
135// --- Rust-core (no-std) ---
136use core::fmt::Debug;
137
138// --- FRAME System ---
139use frame_system::{pallet_prelude::BlockNumberFor, Config, Pallet};
140
141// --- Substrate Primitives ---
142use sp_core::blake2_256;
143use sp_std::vec;
144use sp_runtime::{
145 offchain::storage::{MutateStorageError, StorageValueRef},
146 traits::{One, Saturating, Zero},
147 DispatchError, Vec
148};
149
150// ===============================================================================
151// `````````````````````````````````` CONSTANTS ``````````````````````````````````
152// ===============================================================================
153
154/// Highest known longest-chain head used for fork detection.
155///
156/// If a new block extends past `HEAD_BLOCK`,
157/// it is treated as longest-chain extension.
158///
159/// If another block appears at the same height or lower,
160/// it is treated as a sibling fork.
161///
162/// Sibling fork detection is best-effort:
163///
164/// a lower block may still be the head of its own valid fork,
165/// but it is treated as a sibling branch of the nearest known path.
166///
167/// This intentionally favors fewer branch creations
168/// and reduced storage growth over perfect historical fork reconstruction.
169pub const HEAD_BLOCK: &'static [u8] = b"LOCAL_HEAD_BLOCK";
170
171// ===============================================================================
172// `````````````````````````````````` STRUCTURES `````````````````````````````````
173// ===============================================================================
174
175/// Fork branch details pertaining to a block and the
176/// specialized scope state defined by the pallet/module
177/// for which the local fork graph exists.
178///
179/// Each branch represents the traversed path generation
180/// from its local root.
181///
182/// This root is not necessarily true genesis, but in the
183/// best-case scenario it is the original genesis ancestry.
184///
185/// If the next block is a direct child on top of the same path,
186/// the same branch structure is shared and only the branch head
187/// moves forward.
188///
189/// ```text
190/// Direct child progression:
191///
192/// A -> B -> C -> D
193/// same branch
194///
195/// only:
196/// head = D
197/// ```
198///
199/// If the next block becomes a sibling block, it constitutes
200/// a fork and a new branch is created.
201///
202/// That new branch takes an [`Accrete`] over the previous
203/// scope state so all further state becomes localized
204/// to that fork path.
205///
206/// ```text
207/// Sibling fork:
208///
209/// A -> B -> C
210/// |-- D
211/// |-- D'
212///
213/// D = original branch
214/// D' = new sibling branch
215///
216/// scope(D') = scope(D).accrete()
217/// ```
218///
219/// This structure also stores enough ancestry details to
220/// manually traverse:
221///
222/// - parent branches
223/// - sibling branches
224/// - nested forks
225///
226/// allowing full local fork graph inspection.
227#[derive(Clone, Debug, Encode, Decode)]
228pub struct Branch<T: Config, S: ForkScopes> {
229 /// Structural parent branch.
230 ///
231 /// `None` for synthetic recovery root branches
232 /// or initial local roots.
233 pub parent: Option<[u8; 32]>,
234
235 /// Latest block height currently owned
236 /// by this branch.
237 pub head: BlockNumberFor<T>,
238
239 /// Pallet-local fork scope carried by
240 /// this branch lineage.
241 pub scope: S,
242
243 /// Stable ancestry root for deterministic
244 /// branch identity.
245 ///
246 /// Usually this is the parent block hash of the
247 /// block where this fork graph started.
248 ///
249 /// Example:
250 ///
251 /// - genesis block start: parent hash = [0; 32]
252 /// - mid-chain fork start: parent hash of that fork root
253 ///
254 /// Shared across all sibling forks created
255 /// from that same branch origin.
256 pub genesis: [u8; 32],
257
258 /// Fork lineage path from genesis.
259 ///
260 /// ```text
261 /// Root:
262 /// A -> B -> C
263 /// counter = []
264 ///
265 /// First sibling fork:
266 /// A -> B -> C
267 /// |-- C' [0]
268 ///
269 /// Second sibling fork:
270 /// A -> B -> C
271 /// |-- C' [0]
272 /// |-- C'' [1]
273 ///
274 /// Nested fork:
275 /// A -> B -> C
276 /// |-- C' [0]
277 /// |-- D' [0, 0]
278 /// |-- C'' [1]
279 /// |-- D' [1, 0]
280 /// |-- D'' [1, 1]
281 /// ```
282 pub counter: Vec<u32>,
283}
284
285// ===============================================================================
286// ```````````````````````````````````` ENUMS ````````````````````````````````````
287// ===============================================================================
288
289/// Deterministic branch traversal and navigation actions
290/// for moving across the local fork graph.
291#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
292pub enum ForkAction {
293
294 /// Move upward N parent branches.
295 ///
296 /// Before:
297 /// A -> B -> C
298 /// |-- C'
299 /// |-- D'
300 /// ^
301 ///
302 /// Example:
303 /// MoveToParentBranchBack(2)
304 ///
305 /// After:
306 /// A -> B -> C
307 /// ^
308 MoveToParentBranchBack(u32),
309
310 /// Move to the direct parent branch.
311 ///
312 /// Before:
313 /// A -> B -> C
314 /// |-- C'
315 /// ^
316 ///
317 /// After:
318 /// A -> B -> C
319 /// ^
320 MoveToParentBranch,
321
322 /// Move forward into the N-th child branch if it exists.
323 ///
324 /// Before:
325 /// A -> B -> C
326 /// ^
327 /// |-- C'
328 /// |-- C''
329 ///
330 /// Example:
331 /// MoveToChildBranch(1)
332 ///
333 /// After:
334 /// A -> B -> C
335 /// |-- C''
336 /// ^
337 MoveToChildBranch(u32),
338
339 /// Move forward into the first child branch if it exists.
340 ///
341 /// Before:
342 /// A -> B -> C
343 /// ^
344 /// |-- C'
345 ///
346 /// After:
347 /// A -> B -> C
348 /// |-- C'
349 /// ^
350 ///
351 /// Deterministic traversal:
352 /// always child index 0.
353 MoveToNextChildBranch,
354
355 /// Move to a specific sibling branch index.
356 ///
357 /// If unavailable, remain on current branch.
358 ///
359 /// Before:
360 /// A -> B -> C
361 /// |-- D
362 /// | ^
363 /// |-- D'
364 ///
365 /// After:
366 /// A -> B -> C
367 /// |-- D
368 /// |-- D'
369 /// ^
370 MoveToSiblingBranch(u32),
371
372 /// Move to the next sibling branch if it exists.
373 ///
374 /// Before:
375 /// A -> B -> C
376 /// |-- D
377 /// | ^
378 /// |-- D'
379 ///
380 /// After:
381 /// A -> B -> C
382 /// |-- D
383 /// |-- D'
384 /// ^
385 MoveToNextSiblingBranch,
386
387 /// Move to the previous sibling branch if it exists.
388 ///
389 /// Before:
390 /// A -> B -> C
391 /// |-- D
392 /// |-- D'
393 /// ^
394 ///
395 /// After:
396 /// A -> B -> C
397 /// |-- D
398 /// | ^
399 /// |-- D'
400 MoveToPreviousSiblingBranch,
401
402 /// Jump to the oldest reachable ancestry root.
403 ///
404 /// This may be true genesis ancestry or a synthetic
405 /// recovery root depending on recovery history.
406 ///
407 /// Before:
408 /// A -> B -> C
409 /// |-- C' -> D'
410 /// ^
411 ///
412 /// After:
413 /// A
414 /// ^
415 MoveToRootBranch,
416
417}
418
419// ===============================================================================
420// ```````````````````````````````````` TRAITS ```````````````````````````````````
421// ===============================================================================
422
423/// A scope is an abstract area for storing branch-local state and anything
424/// logically related to that fork, such as:
425///
426/// - values
427/// - references
428/// - pointers
429/// - indexes
430/// - cached storage views
431/// - execution context
432/// - fork-local metadata
433/// - lineage-dependent runtime state
434///
435/// `ForkScopes` is generational:
436///
437/// - [`Default`] provides the empty first generation
438/// - [`Portable`] provides codec-safe storage and cloning
439/// - [`Accrete`] allows each new branch to inherit and extend scope
440///
441/// When a new fork branch is created, it accretes from its parent branch,
442/// carrying forward previous generations while starting a fresh local layer.
443///
444/// This makes the newest generation the full reachable state, avoiding
445/// repeated traversal of older branch ancestry during inspection.
446pub trait ForkScopes: Portable + Default + Debug + Accrete {}
447
448/// Blanked Implementation for all types that satisfy the trait impls.
449impl<T> ForkScopes for T where T: Portable + Default + Debug + Accrete {}
450
451/// Fork-aware local branch collection framework for pallet/module state.
452///
453/// This trait allows a pallet (or module using OCWs) to maintain its own
454/// deterministic local fork graph independent of chain consensus.
455///
456/// It is tightly coupled to FRAME System and uses:
457///
458/// - current block number
459/// - parent hash
460/// - historical block hashes
461///
462/// to resolve and recover local branch state.
463///
464/// Each implementation defines:
465///
466/// - its own `TAG` (storage namespace)
467/// - its own `ForkScope` (local state tracked per branch)
468///
469/// This lets every pallet answer:
470///
471/// ```ignore
472/// "what was my local state on this branch?"
473/// ```
474///
475/// Useful for:
476///
477/// - OCW-derived state
478/// - local indexing and aggregation
479/// - branch-aware replay
480/// - deterministic recovery after storage loss
481///
482/// ## OCW execution model
483///
484/// Offchain workers should always begin with:
485///
486/// ```ignore
487/// fn offchain_worker(block_number: BlockNumberFor<T>) {
488/// <Self as ForksHandler<T, MyForkScope>>::start(
489/// Some("my-pallet"),
490/// Some(LogFormatter::default()),
491/// || {
492/// // pallet-specific OCW logic here
493/// }
494/// );
495/// }
496/// ```
497///
498/// [`Self::start`] is the only valid entry point.
499///
500/// It handles:
501///
502/// - longest-chain extension vs sibling fork creation
503/// - recovery of missing or corrupted branch state
504/// - conditions where OCW execution should be skipped
505///
506/// The OCW closure executes only after branch resolution
507/// and recovery have completed.
508///
509/// ## Scope safety
510///
511/// Fork scope state is recoverable, but not permanently trusted.
512///
513/// Recovery may recreate synthetic forks and restore only the
514/// minimum valid state required for continued execution,
515/// not exact historical lineage.
516///
517/// Fork recovery guarantees execution continuity,
518/// not perfect reconstruction.
519///
520/// ## Query model
521///
522/// Branch resolution is intentionally delayed by one block.
523///
524/// The current executing block is not inserted into the fork graph
525/// during its own OCW execution.
526///
527/// Instead:
528///
529/// ```ignore
530/// block N OCW executes
531/// -> block N - 1 is resolved and persisted
532/// ```
533///
534/// because the current block hash is not yet safely available
535/// for deterministic fork routing.
536///
537/// This means queries during OCW execution resolve the
538/// previous block's branch by default.
539///
540/// Additional traversal across parent, sibling,
541/// and canonical ancestry is available through
542/// branch access helpers.
543pub trait ForksHandler<T: Config, S: ForkScopes>:
544 Logging<BlockNumberFor<T>, Logger = DispatchError> + Sized
545{
546 /// Storage namespace prefix for all fork-local keys.
547 ///
548 /// Used to isolate multiple fork-graphs with special scopes.
549 const TAG: &[u8];
550
551 /// Maximum sibling forks allowed from a single branch point.
552 ///
553 /// Once exceeded, no additional sibling branch is created
554 /// and [`Self::max_forks`] is triggered.
555 const MAX_FORKS: u32;
556
557 /// Maximum reverse traversal attempts during recovery.
558 ///
559 /// Used when branch resolution fails and the system
560 /// walks backward to find the nearest recoverable branch state.
561 ///
562 /// Prevents unbounded historical scanning.
563 const MAX_RECOVER_TRAVERSAL: u32;
564
565 /// Start fork resolution for the previous block and execute OCW logic
566 /// inside the resolved branch environment.
567 ///
568 /// This is the only valid OCW entry point.
569 ///
570 /// It determines:
571 ///
572 /// - longest-chain extension vs sibling fork creation
573 /// - divider / branch recovery when storage is missing
574 /// - corruption handling for decode failures
575 /// - mutation conflict promotion into sibling forks
576 /// - conditions where OCW execution should be skipped
577 ///
578 /// Resolution is intentionally delayed by one block:
579 ///
580 /// ```ignore
581 /// block N OCW executes
582 /// -> block N - 1 is resolved and persisted
583 /// ```
584 ///
585 /// because the current block hash is not yet safely available
586 /// for deterministic fork routing.
587 ///
588 /// The provided `ocw` closure runs only after:
589 ///
590 /// - branch resolution is complete
591 /// - recovery (if needed) has finished
592 /// - block -> divider -> branch invariants are restored
593 ///
594 /// This guarantees OCW logic executes only inside a valid
595 /// fork-aware branch context.
596 fn start<F: FnOnce()>(
597 target: Option<&str>,
598 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
599 ocw: F,
600 ) {
601 // Current executing OCW block (block N).
602 //
603 // Fork registration is intentionally delayed by one block,
604 // so this execution resolves and persists: block N - 1
605 //
606 // instead of the currently executing block itself.
607 let block = Pallet::<T>::block_number();
608
609 // Early bootstrap boundary detection.
610 //
611 // At the earliest chain boundary: parent == grand_parent
612 //
613 // because historical hashes are not yet available and
614 // saturating subtraction collapses both values.
615 //
616 // In this state there is not enough ancestry to safely
617 // resolve fork lineage, so execution is skipped.
618 let actual_parent_block = block.saturating_sub(One::one());
619 let actual_grandparent_block = block.saturating_sub(2u8.into());
620
621 if actual_parent_block == actual_grandparent_block {
622 return;
623 }
624
625 // Shift execution target: block N -> resolve block N - 1
626 //
627 // This gives access to:
628 //
629 // - exact current hash of N - 1
630 // - exact parent hash of N - 2
631 //
632 // which removes sibling ambiguity and allows deterministic
633 // branch routing using fully materialized hashes.
634 let block = block.saturating_sub(One::one());
635
636 // Recovery loop.
637 //
638 // Recovery handlers return: Ok(()) => continue
639 // so execution restarts cleanly after repairing storage.
640 //
641 // In practice this usually runs:
642 // - once for normal execution
643 // - twice when recovery is required
644 let _ = loop {
645 // Exact block being inserted into the fork graph.
646 //
647 // Named `current` for branch semantics,
648 // but structurally this is: block N - 1
649 let current = Pallet::<T>::block_hash(block);
650
651 // Parent of the resolved block.
652 //
653 // parent(current) == block N - 2
654 let parent = Pallet::<T>::block_hash(block.saturating_sub(One::one()));
655
656 // Highest known longest-chain boundary.
657 //
658 // Used only for fork detection:
659 //
660 // block <= head -> sibling fork path
661 // block > head -> longest-chain extension
662 //
663 // This is best-effort and intentionally favors:
664 //
665 // - fewer new branches
666 // - lower storage growth
667 //
668 // over perfect historical fork reconstruction.
669 //
670 // (branch a) (branch a) (branch a)
671 // A ----------B-----------C
672 //
673 // is same as
674 // A (branch a)
675 // \
676 // B (branch a.a)
677 // \
678 // C (branch a.b)
679 let head = match Self::get_head() {
680 Some(v) => v,
681 None => {
682 let initial_head = block.saturating_sub(One::one());
683 store_encoded([Self::TAG, HEAD_BLOCK].concat(), &initial_head);
684 initial_head
685 }
686 };
687
688 // Divider lookup: parent -> divider
689 //
690 // Divider is the routing layer between blocks and branches.
691 //
692 // Multiple sequential blocks of the same ancestry:
693 //
694 // A -> B -> C -> D
695 //
696 // share the same branch structure and local scope lineage,
697 // so they resolve into the same branch and only extend its head.
698 //
699 // OCW scope mutation happens only after branch resolution
700 // and graph persistence complete.
701 //
702 // Under normal execution this behaves like sequential runtime:
703 //
704 // blocks of the same ancestry only build forward on top of
705 // the existing branch, so mutation conflicts are not expected.
706 //
707 // A conflict may still happen if a previous OCW is still running
708 // while the next block begins execution asynchronously.
709 //
710 // In that case mutation is resolved by promoting the later writer
711 // into a sibling branch (clone on mutation conflict),
712 // because execution cannot safely wait or abort without first
713 // preserving deterministic fork graph progression.
714 let divider_hash = Self::get_divider(parent);
715
716 // Missing divider means ancestry routing is broken.
717 //
718 // Recovery restores only the minimum valid scope
719 // required for execution continuity, not exact
720 // historical fork reconstruction.
721 if divider_hash.is_none() {
722 match Self::parent_divider_unavailable(block, target, fmt) {
723 Ok(_) => continue,
724 Err(e) => break e,
725 }
726 }
727
728 // Full branch resolution: parent -> dividerkey -> branchkey
729 //
730 // Parent's (N-2) Branch Key
731 let branch_hash = Self::get_branch_hash(parent);
732
733 // SIBLING FORK PATH
734 //
735 // Another block already occupied this height: block <= head
736 // so this is treated as a sibling fork.
737 if block <= head {
738 let prev_branch = match branch_hash {
739 Some(h) => match Self::get_branch(&h) {
740 Some(b) => b,
741
742 // Divider exists but branch payload is missing.
743 None => match Self::parent_branch_unavailable(block, target, fmt) {
744 Ok(_) => continue,
745 Err(e) => break e,
746 },
747 },
748
749 // Divider exists but deeper branch resolution failed.
750 None => match Self::parent_branch_hash_unavailable(block, target, fmt) {
751 Ok(_) => continue,
752 Err(e) => break e,
753 },
754 };
755
756 // New sibling receives:
757 //
758 // - logically accreted scope
759 // - same ancestry root (genesis) continued
760 // - same lineage path until sibling index append
761 let scope = prev_branch.scope.accrete();
762 let genesis = prev_branch.genesis;
763 let mut counter = prev_branch.counter.clone();
764
765 // Find next available sibling slot:
766 //
767 // [x.0], [x.1], [x.2], ...
768 let mut i = 0u32;
769 let k = loop {
770 let mut try_counter = counter.clone();
771 try_counter.push(i);
772
773 let try_branch_hash = branch_key(Self::TAG, genesis, &try_counter);
774
775 // First unused deterministic sibling slot found.
776 if load_value::<Branch<T, S>>(&try_branch_hash).is_none() {
777 break Some(i);
778 }
779
780 i += 1;
781
782 if i > Self::MAX_FORKS {
783 break None;
784 }
785 };
786
787 let Some(new_counter) = k else {
788 match Self::max_forks(block, target, fmt) {
789 Ok(_) => continue,
790 Err(e) => break e,
791 }
792 };
793
794 // Append the sibling fork as a new counter.
795 counter.push(new_counter);
796
797 // Permanent deterministic branch identity: genesis + updated lineage counter
798 let new_branch_hash = branch_key(Self::TAG, genesis, &counter);
799
800 let new_branch = Branch::<T, S> {
801 parent: branch_hash,
802 head: block,
803 scope,
804 genesis,
805 counter,
806 };
807 store_encoded(&new_branch_hash, &new_branch);
808
809 // Divider identity: parent + branchkey
810 //
811 // allows multiple sibling branches from the same parent without overwrite.
812 let new_divider_hash = divider_key(Self::TAG, parent, new_branch_hash);
813 store_encoded(&new_divider_hash, &new_branch_hash);
814
815 // Final block key-derivation: block -> divider -> branch for N-1 block
816 let block_hash = block_key(Self::TAG, current);
817 store_encoded(&block_hash, &new_divider_hash);
818
819 // Run OCW logic and return
820 ocw();
821 return;
822 }
823
824 // LONGEST-CHAIN EXTENSION PATH
825 //
826 // Normal forward progression of the active branch.
827 let Some(located_branch_hash) = branch_hash else {
828 match Self::parent_branch_hash_unavailable(block, target, fmt) {
829 Ok(_) => continue,
830 Err(e) => break e,
831 }
832 };
833
834 // Optimistic mutation:
835 //
836 // mutate existing branch only
837 //
838 // no new branch is created here as its carried forward.
839 let storage_ref = StorageValueRef::persistent(&located_branch_hash);
840 let result = storage_ref.mutate(|result: Result<Option<Branch<T, S>>, _>| {
841 let Ok(maybe) = result else {
842 // Decode corruption is treated the same as
843 // missing branch state and enters recovery.
844 match Self::inherited_branch_decode_error(block, target, fmt) {
845 Ok(_) => return Err(None),
846 Err(e) => return Err(Some(e)),
847 }
848 };
849
850 let mut branch = match maybe {
851 Some(v) => v,
852
853 None => match Self::parent_branch_unavailable(block, target, fmt) {
854 Ok(_) => return Err(None),
855 Err(e) => return Err(Some(e)),
856 },
857 };
858
859 // Extend active branch head only.
860 branch.head = block;
861
862 Ok(branch)
863 });
864
865 match result {
866 Ok(_) => {}
867
868 Err(e) => match e {
869 // Another OCW won the mutation race.
870 //
871 // Do not retry mutation.
872 //
873 // Promote into a sibling conflict branch
874 // to preserve deterministic execution.
875 // Clone on Mutate Conflict
876 MutateStorageError::ConcurrentModification(_) => {
877 match Self::inherited_branch_mutation_conflict(block, target, fmt) {
878 Ok(_) => {
879 ocw();
880 return;
881 }
882 Err(e) => break e,
883 }
884 }
885
886 MutateStorageError::ValueFunctionFailed(e) => match e {
887 Some(e) => break e,
888
889 // Recovery repaired state,
890 // restart cleanly.
891 None => continue,
892 },
893 },
894 }
895
896 // Persist final routing invariant: block -> divider -> branch
897 let block_hash = block_key(Self::TAG, current);
898
899 let Some(divider) = divider_hash else {
900 unreachable!()
901 };
902
903 store_encoded(&block_hash, ÷r);
904
905 // Update longest known boundary only for
906 // forward longest-chain progression.
907 store_encoded([Self::TAG, HEAD_BLOCK].concat(), &block);
908
909 // Run OCW logic and return
910 ocw();
911 return;
912 };
913
914 return;
915 }
916
917 /// Returns the highest known longest-chain boundary used for fork detection.
918 ///
919 /// This is local fork-tracking state, not consensus finality.
920 ///
921 /// Used by [`Self::start`] to decide:
922 ///
923 /// ```ignore
924 /// block <= HEAD_BLOCK -> sibling fork path
925 /// block > HEAD_BLOCK -> longest-chain extension
926 /// ```
927 fn get_head() -> Option<BlockNumberFor<T>> {
928 load_value::<BlockNumberFor<T>>(&[Self::TAG, HEAD_BLOCK].concat())
929 }
930
931 /// Returns the fork-branch key-hash for a persisted block hash.
932 ///
933 /// This should be queried using already finalized block hashes,
934 /// typically the previous block during OCW execution.
935 fn get_branch_hash(hash: T::Hash) -> Option<[u8; 32]> {
936 let divider_hash = Self::get_divider(hash)?;
937 load_value::<[u8; 32]>(÷r_hash)
938 }
939
940 /// Returns the resolved fork-branch data for a persisted block hash.
941 ///
942 /// This should be queried using already finalized block hashes,
943 /// typically the previous block during OCW execution.
944 fn get_block_branch(hash: T::Hash) -> Option<Branch<T, S>> {
945 let branch_hash = Self::get_branch_hash(hash)?;
946 Self::get_branch(&branch_hash)
947 }
948
949 /// Loads a branch directly from its branch hash (key).
950 fn get_branch(branch_hash: &[u8]) -> Option<Branch<T, S>> {
951 load_value::<Branch<T, S>>(branch_hash)
952 }
953
954 /// Returns the structural parent branch of a resolved block.
955 ///
956 /// Useful for manual traversal across fork ancestry.
957 ///
958 /// This should be queried using already finalized block hashes,
959 /// typically the previous block during OCW execution.
960 fn get_prev_branch(hash: T::Hash) -> Option<Branch<T, S>> {
961 let branch = Self::get_block_branch(hash)?;
962 let parent = branch.parent?;
963 Self::get_branch(&parent)
964 }
965
966 fn get_prev_block_branch() -> Option<Branch<T, S>> {
967 let block = Pallet::<T>::block_number();
968 let hash = Pallet::<T>::block_hash(block.saturating_sub(One::one()));
969 Self::get_block_branch(hash)
970 }
971
972 /// Returns the divider for a persisted block hash.
973 ///
974 /// Divider is the routing layer that allows multiple sibling
975 /// branches to coexist from the same parent ancestry.
976 ///
977 /// This should be queried using already finalized block hashes,
978 /// typically the previous block during OCW execution.
979 fn get_divider(hash: T::Hash) -> Option<[u8; 32]> {
980 let hash = block_key(Self::TAG, hash);
981 load_value::<[u8; 32]>(&hash)
982 }
983
984 /// Deterministic traversal across persisted local fork branches.
985 ///
986 /// This moves between already resolved [`Branch`] states created by
987 /// [`ForksHandler::start`] during pallet OCW execution.
988 ///
989 /// Since fork resolution is delayed by one block:
990 ///
991 /// ```ignore
992 /// block N executes
993 /// -> block N - 1 is resolved and persisted
994 /// ```
995 ///
996 /// the current executing block is not yet inserted into the local fork
997 /// graph during its own OCW execution.
998 ///
999 /// For normal OCW access, use [`ForksHandler::get_prev_block_branch`]
1000 /// to retrieve the previous block's (`N - 1`) resolved branch.
1001 ///
1002 /// From that branch, navigation can continue using [`ForkAction`] and
1003 /// [`ForksHandler::transition`], or other helpers such as:
1004 ///
1005 /// - [`ForksHandler::get_block_branch`]
1006 /// - [`ForksHandler::get_prev_branch`]
1007 /// - [`ForksHandler::get_branch`]
1008 ///
1009 /// A branch represents a continuous stream of blocks on the same path:
1010 ///
1011 /// ```text
1012 /// A -> B -> C -> D
1013 /// same branch
1014 /// ```
1015 ///
1016 /// When a division occurs, a new sibling branch is created:
1017 ///
1018 /// ```text
1019 /// A -> B -> C
1020 /// |-- D
1021 /// |-- D'
1022 /// ```
1023 ///
1024 /// Here:
1025 ///
1026 /// - `D` is the original branch continuation
1027 /// - `D'` is the new sibling branch
1028 ///
1029 /// The branch that existed before the split (`A -> B -> C`)
1030 /// becomes the parent branch for both paths.
1031 ///
1032 /// A child branch is any branch created from that fork point.
1033 ///
1034 /// Example:
1035 ///
1036 /// ```text
1037 /// parent branch
1038 /// A -> B -> C
1039 ///
1040 /// child branches
1041 /// |-- D [0]
1042 /// |-- D' [1]
1043 /// ```
1044 ///
1045 /// Nested forks create deeper child branches:
1046 ///
1047 /// ```text
1048 /// A -> B -> C
1049 /// |-- D' [0]
1050 /// |-- E' [0,0]
1051 /// ```
1052 ///
1053 /// This function performs branch navigation, not direct block traversal,
1054 /// so block numbers are not passed here.
1055 ///
1056 /// If block position is needed, it can be inferred from:
1057 ///
1058 /// - the branch [`Branch::head`]
1059 /// - the previously resolved branch head
1060 ///
1061 /// Traversal uses [`ForkAction`] to move between:
1062 ///
1063 /// - parent branches
1064 /// - child branches
1065 /// - sibling branches
1066 /// - root ancestry
1067 ///
1068 /// Returns `Some(branch)` if the target exists,
1069 /// otherwise `None`.
1070 fn transition(
1071 branch: &Branch<T, S>,
1072 action: ForkAction,
1073 ) -> Option<Branch<T, S>> {
1074 match action {
1075
1076 ForkAction::MoveToParentBranch => {
1077 let parent_key = branch.parent?;
1078 let parent_branch = load_value::<Branch<T, S>>(&parent_key)?;
1079 Some(parent_branch)
1080 },
1081
1082 ForkAction::MoveToParentBranchBack(n) => {
1083 if n.is_zero() {
1084 return Some(branch.clone());
1085 }
1086 let mut current_branch = branch.clone();
1087 for _ in 0..n {
1088 let next =
1089 Self::transition(¤t_branch, ForkAction::MoveToParentBranch)?;
1090 current_branch = next;
1091 }
1092 Some(current_branch)
1093 },
1094
1095
1096 ForkAction::MoveToChildBranch(index) => {
1097 let mut child_counter = branch.counter.clone();
1098 child_counter.push(index);
1099
1100 // branch_key is now called with Self::TAG so the key matches
1101 // exactly what start() stored - the bug in the old Branch impl
1102 // was that it omitted the tag entirely.
1103 let child_key = branch_key(Self::TAG, branch.genesis, &child_counter);
1104 let child_branch = load_value::<Branch<T, S>>(&child_key)?;
1105 Some(child_branch)
1106 },
1107
1108 ForkAction::MoveToNextChildBranch => {
1109 Self::transition(branch, ForkAction::MoveToChildBranch(0))
1110 },
1111
1112
1113 ForkAction::MoveToSiblingBranch(index) => {
1114 let sibling_counter = if branch.counter.is_empty() {
1115 // Root branch - children/siblings live at counter [index].
1116 vec![index]
1117 } else {
1118 // Replace last element with the target index.
1119 let mut c = branch.counter.clone();
1120 *c.last_mut()? = index;
1121 c
1122 };
1123
1124 // Bail if the computed counter is identical to the current one
1125 // (caller asked to move to the branch they are already on).
1126 if sibling_counter == branch.counter {
1127 return None;
1128 }
1129
1130 let sibling_key = branch_key(Self::TAG, branch.genesis, &sibling_counter);
1131 let sibling_branch = load_value::<Branch<T, S>>(&sibling_key)?;
1132 Some(sibling_branch)
1133 },
1134
1135 ForkAction::MoveToNextSiblingBranch => {
1136 let next_index = branch.counter.last().map(|k| k.saturating_add(One::one())).unwrap_or(0);
1137 Self::transition(branch, ForkAction::MoveToSiblingBranch(next_index))
1138 },
1139
1140 ForkAction::MoveToPreviousSiblingBranch => {
1141 let last = *branch.counter.last()?;
1142 if last.is_zero() {
1143 return None;
1144 }
1145 Self::transition(branch, ForkAction::MoveToSiblingBranch(last.saturating_sub(One::one())))
1146 },
1147
1148 ForkAction::MoveToRootBranch => {
1149 let mut current_branch = branch.clone();
1150 loop {
1151 if current_branch.parent.is_none() {
1152 return Some(current_branch);
1153 }
1154 match Self::transition(¤t_branch, ForkAction::MoveToParentBranch) {
1155 Some(parent) => {
1156 current_branch = parent;
1157 },
1158 // Parent key was set but branch payload is missing from
1159 // storage - return the last successfully loaded branch.
1160 None => return Some(current_branch),
1161 }
1162 }
1163 },
1164 }
1165 }
1166
1167 /// Derives a 32-byte scope key for a given scope item.
1168 ///
1169 /// Delegates to [`Accrete::make_key`] which produces a stable, content-addressed
1170 /// key from the item.
1171 ///
1172 /// The same item always produces the same key regardless
1173 /// of the block or fork it is called from, making scope keys fork-independent.
1174 fn gen_scope_item_key(
1175 item: &S::Item,
1176 ) -> [u8; 32] {
1177 S::make_key(item)
1178 }
1179
1180 /// Returns `true` if the given scope key exists in the current fork's branch.
1181 ///
1182 /// Resolves the branch for `block - 1` via [`Self::get_prev_block_branch`]
1183 /// and checks both the **local** scope (items written on this exact branch)
1184 /// and the **inherited** scope (items promoted from ancestor branches via
1185 /// `accrete()`).
1186 ///
1187 /// Returns `Err(OCWForksNotEnabled)` if no branch exists for the previous block,
1188 /// which indicates the fork graph has not been initialized via `ForksHandler::start`.
1189 fn scope_item_exists(
1190 key: &[u8; 32],
1191 target: Option<&str>,
1192 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1193 ) -> Result<bool, Self::Logger> {
1194 let Some(branch) = Self::get_prev_block_branch() else {
1195 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1196 &Self::forks_not_enabled(),
1197 Pallet::<T>::block_number(),
1198 target,
1199 fmt,
1200 ));
1201 };
1202
1203 if branch.scope.exists_in_local(&key) {
1204 return Ok(true);
1205 }
1206
1207 if branch.scope.exists_in_inherited(&key) {
1208 return Ok(true);
1209 }
1210
1211 Ok(false)
1212 }
1213
1214 /// Registers a scope item in the **local** scope of the current branch.
1215 ///
1216 /// Resolves the branch for `block - 1` using `block_hash(block - 1)` as
1217 /// the lookup key.
1218 ///
1219 /// The item is added to `branch.scope.local_keys` so it is visible to
1220 /// [`Self::scope_item_exists`] on the same branch and propagates into
1221 /// `inherited_keys` of any child branch created via `accrete()` during
1222 /// the next `ForksHandler::start` call.
1223 ///
1224 /// Returns the 32-byte scope key assigned to the item.
1225 ///
1226 /// Returns:
1227 /// - `Err(OCWForksNotEnabled)` if `block_hash(block - 1)` has no
1228 /// corresponding branch entry, meaning `ForksHandler::start` has not yet
1229 /// run at the current block.
1230 /// - `Err(OCWForksInconsistent)` if the branch hash resolves
1231 /// but the branch itself cannot be loaded.
1232 fn add_to_scope(
1233 item: S::Item,
1234 target: Option<&str>,
1235 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1236 ) -> Result<[u8; 32], Self::Logger> {
1237 let block = Pallet::<T>::block_number()
1238 .saturating_sub(One::one());
1239
1240 let hash = Pallet::<T>::block_hash(block);
1241
1242 let Some(branch_hash) = Self::get_branch_hash(hash) else {
1243 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1244 &Self::forks_not_enabled(),
1245 Pallet::<T>::block_number(),
1246 target,
1247 fmt,
1248 ));
1249 };
1250
1251 let Some(mut branch) = Self::get_branch(&branch_hash) else {
1252 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1253 &Self::inconsistent_forks(),
1254 Pallet::<T>::block_number(),
1255 target,
1256 fmt,
1257 ));
1258 };
1259
1260 let key = branch.scope.add_to_local(item);
1261
1262 store_encoded(&branch_hash, &branch);
1263
1264 Ok(key)
1265 }
1266
1267 /// Removes a scope item from the current branch's local or inherited scope.
1268 ///
1269 /// Resolves the branch for `block - 1` and removes the key from whichever
1270 /// scope layer it occupies:
1271 ///
1272 /// - If the key is in `local_keys`, it is removed directly and the branch
1273 /// is persisted.
1274 /// - If the key is in `inherited_keys`, it is removed from the inherited
1275 /// layer and the branch is persisted.
1276 /// - If the key is in neither layer, the call is a no-op and returns `Ok(())`.
1277 ///
1278 /// Returns:
1279 /// - `Err(OCWForksNotEnabled)` if the branch cannot be resolved.
1280 /// - `Err(OCWForksInconsistent)` if the branch hash exists but the
1281 /// branch itself cannot be loaded.
1282 fn remove_from_scope(
1283 key: &[u8; 32],
1284 target: Option<&str>,
1285 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1286 ) -> Result<(), Self::Logger> {
1287 let block = Pallet::<T>::block_number()
1288 .saturating_sub(One::one());
1289
1290 let hash = Pallet::<T>::block_hash(block);
1291
1292 let Some(branch_hash) = Self::get_branch_hash(hash) else {
1293 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1294 &Self::forks_not_enabled(),
1295 Pallet::<T>::block_number(),
1296 target,
1297 fmt,
1298 ));
1299 };
1300
1301 let Some(mut branch) = Self::get_prev_block_branch() else {
1302 return Err(<Self as Logging<BlockNumberFor<T>>>::error(
1303 &Self::inconsistent_forks(),
1304 Pallet::<T>::block_number(),
1305 target,
1306 fmt,
1307 ));
1308 };
1309
1310 if branch.scope.exists_in_local(&key) {
1311 branch.scope.remove_from_local(&key);
1312
1313 store_encoded(&branch_hash, &branch);
1314
1315 return Ok(());
1316 }
1317
1318 if !branch.scope.exists_in_inherited(&key) {
1319 return Ok(());
1320 }
1321
1322 branch.scope.remove_from_inherited(&key);
1323
1324 store_encoded(&branch_hash, &branch);
1325
1326 Ok(())
1327 }
1328
1329 /// Returns the error used when fork-aware storage is accessed before
1330 /// `ForksHandler::start` has initialized the fork graph.
1331 fn forks_not_enabled() -> DispatchError;
1332
1333 /// Returns the error used when the fork graph is in an inconsistent state.
1334 fn inconsistent_forks() -> DispatchError;
1335
1336 /// Recovery path when a target block's parent block
1337 /// does not have a resolvable branch available.
1338 ///
1339 /// The target block is usually (N-1) and parent (N-2)
1340 ///
1341 /// Storage corruption may permanently lose the parent's
1342 /// local fork scope for that generation, which cannot be
1343 /// reconstructed from chain history alone.
1344 ///
1345 /// Recovery walks backward to the nearest recoverable branch
1346 /// and restores only the minimum valid scope required for
1347 /// execution continuity.
1348 ///
1349 /// This is intentionally scope-first, not lineage-first:
1350 /// synthetic recovery branches may be created instead of
1351 /// exact historical fork reconstruction.
1352 fn parent_branch_hash_unavailable(
1353 block: BlockNumberFor<T>,
1354 _target: Option<&str>,
1355 _fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1356 ) -> Result<(), Self::Logger> {
1357 // Start recovery from: block - 2
1358 //
1359 // because the missing branch belongs to: parent(block)
1360 //
1361 // and we must search older ancestry for the nearest
1362 // recoverable branch state.
1363 let mut recoverer = block.saturating_sub(2u8.into());
1364
1365 let mut recovered = None;
1366
1367 // Prevent unbounded historical scanning.
1368 let mut attempts = 0u32;
1369
1370 loop {
1371 // Hard recovery bound reached.
1372 if attempts >= Self::MAX_RECOVER_TRAVERSAL {
1373 break;
1374 }
1375
1376 // Genesis underflow boundary.
1377 //
1378 // Normally unreachable because genesis-adjacent blocks
1379 // are skipped during regular execution of start().
1380 //
1381 // Recovery still handles this path explicitly so synthetic
1382 // branches can be created for those skipped genesis ranges also.
1383 if recoverer.is_zero() {
1384 break;
1385 }
1386
1387 // Try resolving: recover_via -> divider -> branch
1388 //
1389 // for an older known block.
1390 let recover_via = Pallet::<T>::block_hash(recoverer.saturating_sub(One::one()));
1391
1392 // First valid recoverable branch found.
1393 if let Some(branch) = Self::get_block_branch(recover_via) {
1394 recovered = Some(branch);
1395 break;
1396 }
1397
1398 // Continue walking backward.
1399 recoverer = recoverer.saturating_sub(One::one());
1400 attempts += 1;
1401 }
1402
1403 let scope = match recovered {
1404 // Valid older branch found.
1405 //
1406 // Recovery continues from its accreted scope.
1407 //
1408 // Any local scope that originally belonged to the
1409 // missing branch being recovered is permanently lost
1410 // and cannot be reconstructed from chain history.
1411 //
1412 // We therefore continue from the nearest valid
1413 // recoverable ancestor scope instead.
1414 Some(branch) => branch.scope.accrete(),
1415
1416 // No recoverable branch exists.
1417 //
1418 // Create a synthetic local branches so execution
1419 // can continue safely.
1420 //
1421 // Example:
1422 //
1423 // Real history (lost):
1424 //
1425 // A -> B -> C -> D
1426 //
1427 // After corruption:
1428 //
1429 // A -> ? -> ? -> D
1430 //
1431 // Recovery:
1432 //
1433 // A -> B
1434 //
1435 // C* D*
1436 //
1437 // where:
1438 //
1439 // * = synthetic recovery branches
1440 //
1441 // Exact lineage is unknown, so recovery restores
1442 // independent best-case scope accreted roots instead of inventing
1443 // unverifiable ancestry.
1444 None => {
1445 // This path does not recover the full lineage because:
1446 //
1447 // - no earlier recoverable branch exists, or
1448 // - MAX_RECOVER_TRAVERSAL bound was reached, or
1449 // - block traveral underflowed, indicating genesis blocks for recovery
1450 //
1451 // In that case only the immediate parent of the
1452 // target block is recovered as a synthetic root branch
1453 // with an empty local scope.
1454
1455 let parent_block = block.saturating_sub(One::one());
1456 // Missing parent block being reconstructed.
1457 let parent = Pallet::<T>::block_hash(parent_block);
1458
1459 // Used only as deterministic synthetic root anchor.
1460 let grand_parent = Pallet::<T>::block_hash(block.saturating_sub(2u8.into()));
1461
1462 // Recovery starts from empty local scope.
1463 //
1464 // Exact historical lineage cannot be proven,
1465 // so we do not attempt full lineage reconstruction.
1466 //
1467 // The synthetic root is anchored at the recovery
1468 // block's parent ancestry:
1469 //
1470 // target block = block
1471 // parent = block - 1 (recover)
1472 // grand_parent = block - 2
1473 //
1474 // Since we are recovering the missing parent branch,
1475 // genesis is derived from the parent's parent
1476 // (`grand_parent`) as the deterministic recovery root.
1477 let (scope, genesis) = (
1478 S::default(),
1479 grand_parent.encode().using_encoded(blake2_256),
1480 );
1481
1482 let branch = Branch::<T, S> {
1483 parent: None,
1484 head: parent_block,
1485 scope,
1486 genesis,
1487 counter: Vec::new(),
1488 };
1489
1490 // Synthetic independent recovery root.
1491 let branch_hash = branch_key(Self::TAG, genesis, &[]);
1492
1493 store_encoded(&branch_hash, &branch);
1494
1495 // Restore: parent -> divider -> branch
1496 let divider_hash = divider_key(Self::TAG, parent, branch_hash);
1497
1498 store_encoded(÷r_hash, &branch_hash);
1499
1500 let block_hash = block_key(Self::TAG, parent);
1501
1502 store_encoded(&block_hash, ÷r_hash);
1503
1504 return Ok(());
1505 }
1506 };
1507
1508 // Forward rebuild begins immediately after
1509 // the last recoverable point.
1510 let mut target = recoverer.saturating_add(One::one());
1511 let mut branchkey = None;
1512
1513 while target < block {
1514 // Block being synthetically restored.
1515 let current = Pallet::<T>::block_hash(target);
1516
1517 let parent = Pallet::<T>::block_hash(target.saturating_sub(One::one()));
1518
1519 let branch_hash = match branchkey {
1520 Some(h) => h,
1521 None => {
1522 // Each recovered step is treated as an
1523 // independent synthetic scope branch.
1524 //
1525 // This is intentionally scope-first recovery.
1526 //
1527 // Any original local scope that belonged to these
1528 // historical blocks, if it once existed locally,
1529 // is permanently lost and cannot be reconstructed.
1530 //
1531 // Recovery restores only execution continuity,
1532 // not the exact historical fork-local state.
1533
1534 let genesis = parent.encode().using_encoded(blake2_256);
1535
1536 let branch = Branch::<T, S> {
1537 parent: Self::get_branch_hash(Pallet::<T>::block_hash(recoverer.saturating_sub(One::one()))),
1538 head: target,
1539 scope: scope.clone(),
1540 genesis,
1541 counter: Vec::new(),
1542 };
1543
1544 let key = branch_key(Self::TAG, genesis, &[]);
1545
1546 store_encoded(&key, &branch);
1547
1548 branchkey = Some(key);
1549
1550 key
1551 }
1552 };
1553
1554 // Restore routing invariant: block -> divider -> branch
1555 let divider_hash = divider_key(Self::TAG, parent, branch_hash);
1556
1557 store_encoded(÷r_hash, &branch_hash);
1558
1559 let block_hash = block_key(Self::TAG, current);
1560
1561 store_encoded(&block_hash, ÷r_hash);
1562
1563 target = target.saturating_add(One::one());
1564 }
1565
1566 Ok(())
1567 }
1568
1569 /// Recovery path when a divider exists for a target block's (N-1)
1570 /// parent (N-2) but the resolved branch data is missing.
1571 ///
1572 /// The stale divider is cleared first, then recovery
1573 /// falls back to [`Self::parent_branch_hash_unavailable`]
1574 /// to rebuild the minimum valid branch state.
1575 fn parent_branch_unavailable(
1576 block: BlockNumberFor<T>,
1577 target: Option<&str>,
1578 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1579 ) -> Result<(), Self::Logger> {
1580 // To Resolve: parent -> divider -> branch
1581 let parent = Pallet::<T>::block_hash(block.saturating_sub(1u8.into()));
1582
1583 // Divider still exists, so ancestry routing is present.
1584 //
1585 // Only the actual branch payload is missing.
1586 let divider_hash = match Self::get_divider(parent) {
1587 Some(v) => v,
1588
1589 // Divider is also missing.
1590 //
1591 // This is no longer a branch-only failure and must
1592 // fall back to full ancestry recovery.
1593 None => {
1594 return Self::parent_branch_hash_unavailable(block, target, fmt);
1595 }
1596 };
1597
1598 // Divider points to invalid / missing branch state.
1599 //
1600 // Clear stale routing first so recovery can rebuild:
1601 //
1602 // block -> divider -> branch
1603 //
1604 // cleanly without reusing corrupted ancestry.
1605 let mut divider_ref = StorageValueRef::persistent(÷r_hash);
1606
1607 divider_ref.clear();
1608
1609 // Delegate to normal branch recovery.
1610 Self::parent_branch_hash_unavailable(block, target, fmt)
1611 }
1612
1613 /// Recovery path when optimistic branch mutation fails due to
1614 /// concurrent OCW modification.
1615 ///
1616 /// Unlike other recovery paths, this handles the target block (N-1)
1617 /// itself, not its missing parent (N-2) ancestry. It does not return
1618 /// control to [`Self::start`] for retry, because no parent recovery
1619 /// is needed.
1620 ///
1621 /// Instead, it directly performs branch update for the target
1622 /// block by cloning the conflicting structure - the inherited
1623 /// branch from parent into a new sibling branch.
1624 ///
1625 /// The conflicting writer keeps the original branch,
1626 /// while the later writer continues on a cloned sibling fork.
1627 ///
1628 /// This preserves deterministic execution without retrying
1629 /// mutation on already committed branch state.
1630 fn inherited_branch_mutation_conflict(
1631 block: BlockNumberFor<T>,
1632 target: Option<&str>,
1633 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1634 ) -> Result<(), Self::Logger> {
1635 // Current block being persisted.
1636 let current = Pallet::<T>::block_hash(block);
1637
1638 // Parent of the target block.
1639 // Used to resolve: parent -> divider -> branch(parent)
1640 //
1641 // because the conflict belongs to the parent branch lineage.
1642 let parent = Pallet::<T>::block_hash(block.saturating_sub(One::one()));
1643
1644 // Locate the existing branch that won the mutation race.
1645 let branch_hash = match Self::get_branch_hash(parent) {
1646 Some(v) => v,
1647
1648 // Parent ancestry is already broken.
1649 None => {
1650 return Self::parent_branch_hash_unavailable(block, target, fmt);
1651 }
1652 };
1653
1654 let prev_branch = match Self::get_branch(&branch_hash) {
1655 Some(v) => v,
1656
1657 // Divider exists but branch payload is missing.
1658 None => {
1659 return Self::parent_branch_unavailable(block, target, fmt);
1660 }
1661 };
1662
1663 // Clone logical scope from the already-existing branch.
1664 //
1665 // Conflict resolution becomes:
1666 //
1667 // existing branch => sibling branch
1668 let scope = prev_branch.scope.accrete();
1669 let genesis = prev_branch.genesis;
1670 let mut counter = prev_branch.counter.clone();
1671
1672 // Find the next deterministic sibling slot.
1673 //
1674 // Since mutation already happened on the original branch,
1675 // we must force this execution into a sibling branch.
1676 //
1677 // Reusing the same branch would overwrite already
1678 // committed branch state.
1679 //
1680 // Example:
1681 //
1682 // Original:
1683 //
1684 // A -> B -> C -> D
1685 //
1686 // where:
1687 //
1688 // A starts the branch lineage
1689 //
1690 // If C and D mutate concurrently:
1691 //
1692 // writer 1 -> keeps original branch
1693 // writer 2 -> mutation conflict
1694 //
1695 // Recovery becomes:
1696 //
1697 // A -> B -> C
1698 // |-- C' -> D
1699 //
1700 // We effectively abandon continuation on the original
1701 // branch and create a cloned sibling fork from the
1702 // last safe branch point.
1703 //
1704 // The cloned branch keeps:
1705 //
1706 // - same scope lineage
1707 // - same genesis
1708 //
1709 // and only receives a new sibling counter:
1710 //
1711 // [0], [1], [2], ...
1712 let mut i = 0u32;
1713
1714 let next_counter = loop {
1715 let mut try_counter = counter.clone();
1716 try_counter.push(i);
1717
1718 let try_branch_hash = branch_key(Self::TAG, genesis, &try_counter);
1719
1720 // First empty sibling slot found.
1721 if load_value::<Branch<T, S>>(&try_branch_hash).is_none() {
1722 break Some(i);
1723 }
1724
1725 i += 1;
1726
1727 if i > Self::MAX_FORKS {
1728 break None;
1729 }
1730 };
1731
1732 let Some(new_counter) = next_counter else {
1733 return Self::max_forks(block, target, fmt);
1734 };
1735
1736 counter.push(new_counter);
1737
1738 // New deterministic sibling branch identity.
1739 let new_branch_hash = branch_key(Self::TAG, genesis, &counter);
1740
1741 let new_branch = Branch::<T, S> {
1742 parent: Some(branch_hash),
1743 head: block,
1744 scope,
1745 genesis,
1746 counter,
1747 };
1748
1749 store_encoded(&new_branch_hash, &new_branch);
1750
1751 // New sibling divider: parent + branch
1752 let divider_hash = divider_key(Self::TAG, parent, new_branch_hash);
1753
1754 store_encoded(÷r_hash, &new_branch_hash);
1755
1756 // Final resolution: block -> divider -> branch
1757 let block_hash = block_key(Self::TAG, current);
1758
1759 store_encoded(&block_hash, ÷r_hash);
1760
1761 // Conflict branch becomes the new reachable head.
1762 store_encoded([Self::TAG, HEAD_BLOCK].concat(), &block);
1763
1764 Ok(())
1765 }
1766
1767 /// Recovery path when divider routing is missing for the
1768 /// target block's (N-1) parent (N-2).
1769 ///
1770 /// Since divider loss breaks ancestry routing entirely,
1771 /// recovery falls back directly to
1772 /// [`Self::parent_branch_hash_unavailable`].
1773 fn parent_divider_unavailable(
1774 block: BlockNumberFor<T>,
1775 target: Option<&str>,
1776 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1777 ) -> Result<(), Self::Logger> {
1778 Self::parent_branch_hash_unavailable(block, target, fmt)
1779 }
1780
1781 /// Recovery path when branch decoding fails for the
1782 /// target block's (N-1) inherited branch from parent (N-2)
1783 /// for extension.
1784 ///
1785 /// This means the branch exists in storage,
1786 /// but its payload is corrupted or unreadable.
1787 ///
1788 /// It is treated the same as a missing branch and
1789 /// delegated to [`Self::parent_branch_unavailable`].
1790 fn inherited_branch_decode_error(
1791 block: BlockNumberFor<T>,
1792 target: Option<&str>,
1793 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1794 ) -> Result<(), Self::Logger> {
1795 Self::parent_branch_unavailable(block, target, fmt)
1796 }
1797
1798 /// Triggered when no additional sibling branch slot
1799 /// can be allocated under the configured limit as the sibling
1800 /// of the target block's (N-1) parent (N-2).
1801 ///
1802 /// This occurs when:
1803 ///
1804 /// ```ignore
1805 /// sibling_count > MAX_FORKS
1806 /// ```
1807 ///
1808 /// and branch creation must stop.
1809 fn max_forks(
1810 block: BlockNumberFor<T>,
1811 target: Option<&str>,
1812 fmt: Option<LogFormatter<BlockNumberFor<T>, Self::Level>>,
1813 ) -> Result<(), Self::Logger> {
1814 Err(<Self as Logging<BlockNumberFor<T>>>::error(
1815 &Self::max_forks_error(),
1816 block,
1817 target,
1818 fmt,
1819 ))
1820 }
1821
1822 /// Error returned when fork creation exceeds
1823 /// [`Self::MAX_FORKS`].
1824 fn max_forks_error() -> DispatchError;
1825
1826}
1827
1828// ===============================================================================
1829// `````````````````````````````` PRIVATE UTILITIES ``````````````````````````````
1830// ===============================================================================
1831
1832/// Deterministic storage hash builder.
1833///
1834/// Used for all Persistent Node-Local storage keys.
1835fn make_hash(tag: &[u8], input: impl Encode, suffix: &[u8]) -> [u8; 32] {
1836 let mut source = tag.encode();
1837 source.extend_from_slice(&input.encode());
1838 source.extend_from_slice(suffix);
1839 blake2_256(&source)
1840}
1841
1842/// Persist an encoded value into Persistent Node-Local storage.
1843fn store_encoded<K: AsRef<[u8]>, V: Encode>(key: K, value: &V) {
1844 let storage_ref = StorageValueRef::persistent(key.as_ref());
1845 storage_ref.set(&value);
1846}
1847
1848/// Load and decode a value from Persistent Node-Local storage.
1849fn load_value<V: codec::Decode>(key: &[u8]) -> Option<V> {
1850 let storage_ref = StorageValueRef::persistent(key);
1851 let Ok(result) = storage_ref.get::<V>() else {
1852 return None;
1853 };
1854 result
1855}
1856
1857/// Deterministic branch identity key
1858///
1859/// ```ignore
1860/// genesis + counter lineage -> branchkey
1861/// ```
1862fn branch_key(tag: &[u8], genesis: [u8; 32], counter: &[u32]) -> [u8; 32] {
1863 let mut identity = genesis.encode();
1864
1865 for c in counter {
1866 identity.extend_from_slice(&c.encode());
1867 }
1868
1869 make_hash(tag, &identity, b"branch")
1870}
1871
1872/// Divider identity key
1873///
1874/// ```ignore
1875/// parent + branch -> divider
1876/// ```
1877///
1878/// Allows sibling branches from the same parent.
1879fn divider_key(tag: &[u8], hash: impl Encode, branch_key: [u8; 32]) -> [u8; 32] {
1880 let mut identity = hash.encode();
1881 identity.extend_from_slice(&branch_key.encode());
1882
1883 make_hash(tag, &identity, b"divider")
1884}
1885
1886/// Block routing identity key
1887///
1888/// ```ignore
1889/// block -> divider
1890/// ```
1891fn block_key(tag: &[u8], hash: impl Encode) -> [u8; 32] {
1892 let identity = hash.encode();
1893 make_hash(tag, &identity, b"block")
1894}
1895
1896
1897// ===============================================================================
1898// `````````````````````````````````` UNIT TESTS `````````````````````````````````
1899// ===============================================================================
1900
1901#[cfg(test)]
1902mod tests {
1903
1904 // -------------------------------------------------------------------------
1905 // ```````````````````````````````` IMPORTS ````````````````````````````````
1906 // -------------------------------------------------------------------------
1907 use super::*;
1908
1909 // --- FRAME Support ---
1910 use frame_support::derive_impl;
1911
1912 // --- FRAME System ---
1913 use frame_system::pallet_prelude::BlockNumberFor;
1914
1915 // --- Substrate crates ---
1916 use sp_core::offchain::{
1917 testing::{TestOffchainExt, TestTransactionPoolExt},
1918 OffchainDbExt, OffchainWorkerExt, TransactionPoolExt,
1919 };
1920 use sp_io::TestExternalities;
1921 use sp_runtime::{
1922 offchain::storage::StorageValueRef,
1923 traits::{BlakeTwo256, Hash},
1924 AccountId32, BuildStorage, DispatchError,
1925 };
1926
1927 // -------------------------------------------------------------------------
1928 // `````````````````````````````` MOCK RUNTIME `````````````````````````````
1929 // -------------------------------------------------------------------------
1930
1931 pub type Block = frame_system::mocking::MockBlock<Test>;
1932
1933 #[frame_support::runtime]
1934 pub mod runtime {
1935 #[runtime::runtime]
1936 #[runtime::derive(
1937 RuntimeCall,
1938 RuntimeEvent,
1939 RuntimeError,
1940 RuntimeOrigin,
1941 RuntimeFreezeReason,
1942 RuntimeHoldReason,
1943 RuntimeSlashReason,
1944 RuntimeLockId,
1945 RuntimeTask,
1946 RuntimeViewFunction
1947 )]
1948 pub struct Test;
1949
1950 #[runtime::pallet_index(0)]
1951 pub type System = frame_system::Pallet<Test>;
1952 }
1953
1954 #[derive_impl(frame_system::config_preludes::TestDefaultConfig)]
1955 impl frame_system::Config for Test {
1956 type Block = Block;
1957 type AccountId = AccountId32;
1958 type Lookup = sp_runtime::traits::IdentityLookup<Self::AccountId>;
1959 }
1960
1961 // -------------------------------------------------------------------------
1962 // `````````````````````````` OCW TEST ENVIRONMENT `````````````````````````
1963 // -------------------------------------------------------------------------
1964
1965 fn new_ocw_ext() -> TestExternalities {
1966 let storage = frame_system::GenesisConfig::<Test>::default()
1967 .build_storage()
1968 .unwrap();
1969
1970 let mut ext = TestExternalities::new(storage);
1971 ext.execute_with(|| System::set_block_number(1u64));
1972
1973 let (offchain, _state) = TestOffchainExt::new();
1974 ext.register_extension(OffchainWorkerExt::new(offchain.clone()));
1975 ext.register_extension(OffchainDbExt::new(offchain));
1976
1977 let (pool, _) = TestTransactionPoolExt::new();
1978 ext.register_extension(TransactionPoolExt::new(pool));
1979
1980 ext
1981 }
1982
1983 // -------------------------------------------------------------------------
1984 // `````````````````````````` MOCK IMPL FORKSCOPES `````````````````````````
1985 // -------------------------------------------------------------------------
1986
1987 // A minimal scope implementation that actually tracks local and inherited
1988 // keys
1989 #[derive(Clone, Debug, Default, Encode, Decode)]
1990 struct TestScope {
1991 local: std::collections::BTreeSet<[u8; 32]>,
1992 inherited: std::collections::BTreeSet<[u8; 32]>,
1993 }
1994
1995 impl Accrete for TestScope {
1996 type Item = Vec<u8>;
1997
1998 fn accrete(&self) -> Self {
1999 let mut inh = self.inherited.clone();
2000 inh.extend(self.local.iter().copied());
2001 Self { local: std::collections::BTreeSet::new(), inherited: inh }
2002 }
2003
2004 fn inherited(&self) -> Vec<[u8; 32]> { self.inherited.iter().copied().collect() }
2005 fn local(&self) -> Vec<[u8; 32]> { self.local.iter().copied().collect() }
2006
2007 fn add_to_local(&mut self, item: Self::Item) -> [u8; 32] {
2008 let key = Self::make_key(&item);
2009 self.local.insert(key);
2010 key
2011 }
2012
2013 fn exists_in_local(&self, key: &[u8; 32]) -> bool { self.local.contains(key) }
2014 fn exists_in_inherited(&self, key: &[u8; 32]) -> bool { self.inherited.contains(key) }
2015 fn remove_from_local(&mut self, key: &[u8; 32]) { self.local.remove(key); }
2016 fn remove_from_inherited(&mut self, key: &[u8; 32]) { self.inherited.remove(key); }
2017 }
2018
2019 // -------------------------------------------------------------------------
2020 // ```````````````````````````````` CONSTANTS ``````````````````````````````
2021 // -------------------------------------------------------------------------
2022
2023 const TAG: &[u8] = b"test_forks";
2024
2025 // -------------------------------------------------------------------------
2026 // ```````````````````````` MOCK IMPL FORKS-HANDLER ````````````````````````
2027 // -------------------------------------------------------------------------
2028
2029 struct TestForks;
2030
2031 impl ForksHandler<Test, TestScope> for TestForks {
2032 const TAG: &[u8] = b"test_forks";
2033 const MAX_FORKS: u32 = 3;
2034 const MAX_RECOVER_TRAVERSAL: u32 = 10;
2035
2036 fn max_forks_error() -> DispatchError {
2037 DispatchError::Other("max_forks_exceeded")
2038 }
2039
2040 fn forks_not_enabled() -> DispatchError {
2041 DispatchError::Other("forks-not-enabled")
2042 }
2043
2044 fn inconsistent_forks() -> DispatchError {
2045 DispatchError::Other("inconsistent-forks")
2046 }
2047 }
2048
2049 // -------------------------------------------------------------------------
2050 // ```````````````````````````````` HELPERS ````````````````````````````````
2051 // -------------------------------------------------------------------------
2052
2053 /// Returns a deterministic non-zero mock hash for block `n`.
2054 fn mock_hash(n: u64) -> <Test as frame_system::Config>::Hash {
2055 BlakeTwo256::hash(&n.to_le_bytes())
2056 }
2057
2058 /// Populates frame_system::BlockHash for blocks `start..=end`.
2059 fn register_block_hashes(start: u64, end: u64) {
2060 for n in start..=end {
2061 frame_system::BlockHash::<Test>::insert(n, mock_hash(n));
2062 }
2063 }
2064
2065 /// Sets system block number.
2066 fn set_block(n: u64) { System::set_block_number(n) }
2067
2068 /// Reads HEAD from offchain storage.
2069 fn read_head() -> Option<BlockNumberFor<Test>> {
2070 load_value::<BlockNumberFor<Test>>(&[TAG, HEAD_BLOCK].concat())
2071 }
2072
2073 /// Resolves a branch via the full routing chain for block `n`:
2074 /// block_hash(n) -> block_key -> divider -> branch_key -> Branch
2075 fn resolve_branch(n: u64) -> Option<Branch<Test, TestScope>> {
2076 TestForks::get_block_branch(mock_hash(n))
2077 }
2078
2079 /// Reads a branch directly by genesis + counter.
2080 fn branch_by_lineage(
2081 genesis: [u8; 32],
2082 counter: &[u32],
2083 ) -> Option<Branch<Test, TestScope>> {
2084 load_value::<Branch<Test, TestScope>>(&branch_key(TAG, genesis, counter))
2085 }
2086
2087 /// Returns the genesis that the None recovery path derives when
2088 /// grand_parent = block_hash(gp_block).
2089 fn recovery_genesis(gp_block: u64) -> [u8; 32] {
2090 mock_hash(gp_block).encode().using_encoded(blake2_256)
2091 }
2092
2093 // -------------------------------------------------------------------------
2094 // ````````````````````` TS 1 - KEY BUILDER DETERMINISM ````````````````````
2095 // -------------------------------------------------------------------------
2096
2097 /// Behavior: block_key is deterministic, TAG-namespaced, and distinct
2098 /// across different block hashes.
2099 ///
2100 /// Scenario: two block hashes, two TAGs.
2101 #[test]
2102 fn key_builders_block_key_is_deterministic_and_tag_namespaced() {
2103 let h1 = mock_hash(1);
2104 let h2 = mock_hash(2);
2105 let tag_a = b"pallet_a".as_ref();
2106 let tag_b = b"pallet_b".as_ref();
2107
2108 // Deterministic.
2109 assert_eq!(block_key(tag_a, h1), block_key(tag_a, h1));
2110
2111 // Different TAG -> different key.
2112 assert_ne!(block_key(tag_a, h1), block_key(tag_b, h1));
2113
2114 // Different hash -> different key.
2115 assert_ne!(block_key(tag_a, h1), block_key(tag_a, h2));
2116 }
2117
2118 /// Behavior: divider_key differentiates sibling branches from the same
2119 /// parent, which is what prevents them from overwriting each other.
2120 ///
2121 /// Scenario: one parent hash, two different branch keys (siblings).
2122 #[test]
2123 fn key_builders_divider_key_differentiates_siblings_from_same_parent() {
2124 let tag = TAG;
2125 let parent = mock_hash(5);
2126 let branch_a = [0xAAu8; 32];
2127 let branch_b = [0xBBu8; 32];
2128
2129 let d_a = divider_key(tag, parent, branch_a);
2130 let d_b = divider_key(tag, parent, branch_b);
2131
2132 // Two siblings with the same parent must produce distinct divider keys.
2133 assert_ne!(d_a, d_b);
2134
2135 // Deterministic
2136 assert_eq!(d_a, divider_key(tag, parent, branch_a));
2137 }
2138
2139 /// Behavior: branch_key encodes the full counter lineage, so branches at
2140 /// different paths are always stored at distinct keys.
2141 ///
2142 /// Scenario: root [], first fork [0], second fork [1], nested fork [0,0],
2143 /// and same counter under a different genesis.
2144 #[test]
2145 fn key_builders_branch_key_encodes_full_counter_lineage() {
2146 let tag = TAG;
2147 let genesis = recovery_genesis(0);
2148
2149 let k_root = branch_key(tag, genesis, &[]);
2150 let k_fork0 = branch_key(tag, genesis, &[0]);
2151 let k_fork1 = branch_key(tag, genesis, &[1]);
2152 let k_nested = branch_key(tag, genesis, &[0, 0]);
2153
2154 assert_ne!(k_root, k_fork0);
2155 assert_ne!(k_fork0, k_fork1);
2156 assert_ne!(k_fork0, k_nested);
2157
2158 // Deterministic
2159 assert_eq!(k_fork0, branch_key(tag, genesis, &[0]));
2160
2161 // Different genesis -> different key even with same counter.
2162 let genesis2 = recovery_genesis(1);
2163 assert_ne!(branch_key(tag, genesis, &[0]), branch_key(tag, genesis2, &[0]));
2164 }
2165
2166 // -------------------------------------------------------------------------
2167 // ````````````````````````` TS 2 - BOOTSTRAP GUARD ````````````````````````
2168 // -------------------------------------------------------------------------
2169
2170 /// Behavior: start() returns immediately at block 0 - saturating sub
2171 /// collapses parent and grandparent to the same value.
2172 ///
2173 /// Scenario: N=0, actual_parent(0) == actual_grandparent(0).
2174 #[test]
2175 fn bootstrap_guard_skips_block_zero() {
2176 new_ocw_ext().execute_with(|| {
2177 set_block(0);
2178 let mut ran = false;
2179 TestForks::start(None, None, || { ran = true; });
2180 assert!(!ran, "ocw must not run at block 0");
2181 assert!(read_head().is_none());
2182 });
2183 }
2184
2185 /// Behavior: start() returns immediately at block 1.
2186 ///
2187 /// Scenario: N=1, actual_parent(0) == actual_grandparent(0).
2188 #[test]
2189 fn bootstrap_guard_skips_block_one() {
2190 new_ocw_ext().execute_with(|| {
2191 set_block(1);
2192 let mut ran = false;
2193 TestForks::start(None, None, || { ran = true; });
2194 assert!(!ran, "ocw must not run at block 1");
2195 assert!(read_head().is_none());
2196 });
2197 }
2198
2199 /// Behavior: block 2 is the exact lower boundary where start() proceeds.
2200 ///
2201 /// Scenario: guard fires at N=1, passes at N=2.
2202 /// since, actual_parent(1) != actual_grandparent(0)
2203 #[test]
2204 fn bootstrap_guard_exact_boundary_block_two_passes() {
2205 new_ocw_ext().execute_with(|| {
2206 register_block_hashes(0, 2);
2207
2208 set_block(1);
2209 let mut ran = false;
2210 TestForks::start(None, None, || { ran = true; });
2211 assert!(!ran, "block 1 must be skipped by the bootstrap guard");
2212
2213 set_block(2);
2214 let mut ran = false;
2215 TestForks::start(None, None, || { ran = true; });
2216 assert!(ran, "block 2 must pass the bootstrap guard and call ocw");
2217 });
2218 }
2219
2220 // -------------------------------------------------------------------------
2221 // ``````````````````` TS 3 - FRESH GRAPH INITIALISATION ```````````````````
2222 // -------------------------------------------------------------------------
2223
2224 /// Behavior: the first start() call creates a root branch and establishes
2225 /// the full routing invariant for the resolved block.
2226 ///
2227 /// Scenario:
2228 /// (no prior graph)
2229 ///
2230 /// After start(N=2) - resolves block 1:
2231 /// root branch counter=[] head=1 parent=None
2232 /// HEAD = 1
2233 ///
2234 /// Routing: block_hash(1) -> block_key -> divider -> branch
2235 ///
2236 /// Note: block_hash(0) also has routing because recovery stores it as
2237 /// the synthetic root anchor for block 1's parent. Both point to the
2238 /// same branch payload, which has head=1 after the longest-chain mutate.
2239 #[test]
2240 fn fresh_graph_initialisation_at_block_two() {
2241 new_ocw_ext().execute_with(|| {
2242 register_block_hashes(0, 2);
2243
2244 set_block(2);
2245 let mut ran = false;
2246 TestForks::start(None, None, || { ran = true; });
2247 assert!(ran, "ocw must run after fresh graph initialisation");
2248
2249 // HEAD = N-1 = 1.
2250 assert_eq!(read_head(), Some(1));
2251
2252 let root = resolve_branch(1)
2253 .expect("block 1 routing must resolve");
2254
2255 assert!(root.parent.is_none());
2256 assert!(root.counter.is_empty());
2257 assert_eq!(root.head, 1);
2258
2259 // block 0 routes to the same branch as block 1 (synthetic root anchor).
2260 let b0 = resolve_branch(0)
2261 .expect("block 0 routing exists as the synthetic root anchor");
2262 assert_eq!(b0.genesis, root.genesis);
2263 assert_eq!(b0.head, 1);
2264
2265 // No sibling must exist
2266 assert!(
2267 branch_by_lineage(root.genesis, &[0]).is_none(),
2268 "no sibling branch may exist after fresh init"
2269 );
2270 });
2271 }
2272
2273 // -------------------------------------------------------------------------
2274 // `````````````````````` TS 4 - SEQUENTIAL EXTENSION ``````````````````````
2275 // -------------------------------------------------------------------------
2276
2277 /// Behavior: successive start() calls on the same lineage extend the root
2278 /// branch head in-place without creating new branches.
2279 ///
2280 /// Scenario:
2281 /// A(0) -> B(1) -> C(2) -> D(3) => single root branch throughout
2282 ///
2283 /// After start(N=3): root.head=2, HEAD=2
2284 /// After start(N=4): root.head=3, HEAD=3
2285 #[test]
2286 fn sequential_extension_advances_root_branch_head() {
2287 new_ocw_ext().execute_with(|| {
2288 register_block_hashes(0, 5);
2289
2290 set_block(2);
2291 TestForks::start(None, None, || {});
2292
2293 set_block(3);
2294 let mut ran = false;
2295 TestForks::start(None, None, || { ran = true; });
2296
2297 assert!(ran, "ocw must run at block 3");
2298 assert_eq!(read_head(), Some(2));
2299
2300 let b2 = resolve_branch(2).expect("block 2 routing must resolve");
2301 assert_eq!(b2.head, 2);
2302 assert!(b2.counter.is_empty());
2303
2304 // Earlier routing for block 1 must survive and share genesis.
2305 let b1 = resolve_branch(1).expect("block 1 routing must still resolve");
2306 assert_eq!(b1.genesis, b2.genesis);
2307
2308 // No sibling must exist.
2309 assert!(
2310 branch_by_lineage(b2.genesis, &[0]).is_none(),
2311 "no sibling may exist after sequential extension"
2312 );
2313
2314 set_block(4);
2315 let mut ran = false;
2316 TestForks::start(None, None, || { ran = true; });
2317
2318 assert!(ran, "ocw must run at block 4");
2319 assert_eq!(read_head(), Some(3));
2320
2321 let b3 = resolve_branch(3).expect("block 3 routing must resolve");
2322 assert_eq!(b3.head, 3);
2323 assert_eq!(b3.genesis, b2.genesis, "genesis must be stable across extensions");
2324 });
2325 }
2326
2327 // -------------------------------------------------------------------------
2328 // `````````````````````` TS 5 - SIBLING FORK CREATION `````````````````````
2329 // -------------------------------------------------------------------------
2330
2331 /// Behavior: a competing block at height <= HEAD triggers the sibling path.
2332 /// A new branch is created with its own routing chain. HEAD is not updated.
2333 ///
2334 /// Scenario:
2335 /// - Canonical: A(0) -> B(1) -> C(2)->D(3) => HEAD=3
2336 /// - Fork: A(0) -> B(1) -> C'(2) -> D'(3) => competing hashes
2337 ///
2338 /// After fork start(N=4):
2339 /// sibling counter=[0] head=3 parent=Some(fork_root_key)
2340 /// HEAD stays at 3
2341 #[test]
2342 fn sibling_fork_created_when_block_at_or_below_head() {
2343 new_ocw_ext().execute_with(|| {
2344 register_block_hashes(0, 5);
2345
2346 set_block(2);
2347 TestForks::start(None, None, || {});
2348 set_block(3);
2349 TestForks::start(None, None, || {});
2350 set_block(4);
2351 TestForks::start(None, None, || {});
2352 assert_eq!(read_head(), Some(3));
2353
2354 // Capture canonical routing before injecting the fork.
2355 let canonical = resolve_branch(3).expect("canonical block 3 must resolve");
2356
2357 // Inject a competing fork at blocks 2 and 3.
2358 let fork_hash_3 = BlakeTwo256::hash(b"fork_block_3");
2359 let fork_hash_2 = BlakeTwo256::hash(b"fork_block_2");
2360 frame_system::BlockHash::<Test>::insert(3, fork_hash_3);
2361 frame_system::BlockHash::<Test>::insert(2, fork_hash_2);
2362
2363 set_block(4);
2364 let mut ran = false;
2365 TestForks::start(None, None, || { ran = true; });
2366
2367 assert!(ran, "ocw must run on the sibling fork path");
2368
2369 // HEAD must not advance - sibling path does not update it.
2370 assert_eq!(read_head(), Some(3), "HEAD must stay at 3");
2371
2372 // Resolve sibling via the fork's routing chain.
2373 let sibling = TestForks::get_block_branch(fork_hash_3)
2374 .expect("fork_hash_3 must resolve to sibling branch");
2375 assert_eq!(sibling.head, 3);
2376 assert_eq!(sibling.counter, vec![0u32]);
2377 assert!(sibling.parent.is_some());
2378
2379 // Load fork_root via sibling.parent - no genesis derivation needed.
2380 let fork_root_key = sibling.parent.unwrap();
2381 let fork_root = TestForks::get_branch(&fork_root_key)
2382 .expect("fork recovery root must be loadable via sibling.parent");
2383 assert_eq!(fork_root.head, 2);
2384 assert!(fork_root.counter.is_empty());
2385
2386 // Canonical routing must not have been overwritten by the fork.
2387 frame_system::BlockHash::<Test>::insert(3, mock_hash(3));
2388 let canonical_after = resolve_branch(3)
2389 .expect("canonical routing must survive fork creation");
2390 assert_eq!(canonical_after.genesis, canonical.genesis);
2391 });
2392 }
2393
2394 // -------------------------------------------------------------------------
2395 // `````````````````` TS 6 - OCW CLOSURE INVOCATION COUNT ``````````````````
2396 // -------------------------------------------------------------------------
2397
2398 /// Behavior: OCW closure runs exactly once per successful start() call
2399 /// and never during bootstrap-guarded blocks.
2400 ///
2401 /// Scenario: two bootstrap blocks (0,1) and three sequential blocks (2,3,4).
2402 #[test]
2403 fn ocw_closure_runs_exactly_once_per_successful_call() {
2404 new_ocw_ext().execute_with(|| {
2405 register_block_hashes(0, 5);
2406 let mut count = 0u32;
2407
2408 set_block(0);
2409 TestForks::start(None, None, || { count += 1; });
2410 set_block(1);
2411 TestForks::start(None, None, || { count += 1; });
2412 assert_eq!(count, 0, "ocw must never run at blocks 0 or 1");
2413
2414 set_block(2);
2415 TestForks::start(None, None, || { count += 1; });
2416 assert_eq!(count, 1);
2417
2418 set_block(3);
2419 TestForks::start(None, None, || { count += 1; });
2420 assert_eq!(count, 2);
2421
2422 set_block(4);
2423 TestForks::start(None, None, || { count += 1; });
2424 assert_eq!(count, 3);
2425 });
2426 }
2427
2428 // -------------------------------------------------------------------------
2429 // ```````````````````` TS 7 - RECOVERY: MISSING DIVIDER ```````````````````
2430 // -------------------------------------------------------------------------
2431
2432 /// Behavior: a wiped block_key routing entry triggers parent_divider_unavailable,
2433 /// which delegates to parent_branch_hash_unavailable. Recovery rebuilds routing
2434 /// for the gap and HEAD advances correctly.
2435 ///
2436 /// Scenario:
2437 /// - Canonical: A(0) -> B(1) -> C(2) -> D(3) => HEAD=3
2438 /// - Corrupt: block_key for block 3 wiped
2439 ///
2440 /// Recovery rebuilds block 3 routing with parent linkage to block 2.
2441 /// Result: E(4) resolved, HEAD=4
2442 #[test]
2443 fn recovery_missing_divider_rebuilds_routing_and_advances_head() {
2444 new_ocw_ext().execute_with(|| {
2445 register_block_hashes(0, 6);
2446
2447 set_block(2);
2448 TestForks::start(None, None, || {});
2449 set_block(3);
2450 TestForks::start(None, None, || {});
2451 set_block(4);
2452 TestForks::start(None, None, || {});
2453 assert_eq!(read_head(), Some(3));
2454
2455 // Wipe the routing entry for block 3.
2456 StorageValueRef::persistent(&block_key(TAG, mock_hash(3))).clear();
2457 assert!(resolve_branch(3).is_none(), "block 3 routing must be broken after wipe");
2458
2459 set_block(5);
2460 let mut ran = false;
2461 TestForks::start(None, None, || { ran = true; });
2462
2463 assert!(ran, "ocw must run after missing-divider recovery");
2464 assert_eq!(read_head(), Some(4), "HEAD must advance to 4 after recovery");
2465
2466 let b4 = resolve_branch(4).expect("block 4 routing must resolve after recovery");
2467 assert_eq!(b4.head, 4);
2468
2469 // Forward rebuild restores block 3's routing pointing to the same synthetic
2470 // branch. The longest-chain mutate at iteration 2 then advances that
2471 // branch's head from 3 -> 4 in-place, so both block 3 and block 4 route
2472 // to the same payload with head=4.
2473 let b3 = resolve_branch(3).expect("block 3 routing must be rebuilt by forward recovery");
2474 assert_eq!(b3.head, 4, "rebuilt block 3 branch shares payload with block 4 (head advanced to 4 by mutate)");
2475 assert_eq!(
2476 b3.genesis, b4.genesis,
2477 "block 3 and block 4 must share the same synthetic branch lineage"
2478 );
2479 });
2480 }
2481
2482 // -------------------------------------------------------------------------
2483 // ``` TS 8 - RECOVERY: CORRUPTED BRANCH PAYLOAD + STALE DIVIDER CLEANUP ```
2484 // -------------------------------------------------------------------------
2485
2486 /// Behavior: `parent_branch_unavailable` clears the stale divider before
2487 /// delegating to recovery. This test confirms the divider is absent after
2488 /// `start()` completes.
2489 ///
2490 /// The sibling path (block=3 <= head=3) is used so that
2491 /// `parent_branch_unavailable(block=3)` computes:
2492 ///
2493 /// parent = block_hash(2) = mock_hash(2)
2494 ///
2495 /// making divider(mock_hash(2)) the exact key captured and asserted cleared.
2496 ///
2497 /// Scenario:
2498 /// - Canonical: A(0) -> B(1) -> C(2) -> D(3) => HEAD=3
2499 /// - Corrupt: branch payload wiped, routing intact
2500 ///
2501 /// start(N=4) again: block=3 <= head=3 -> sibling path
2502 /// get_branch(branch_key) -> None
2503 /// parent_branch_unavailable clears divider(mock_hash(2))
2504 /// recovery runs, sibling created, HEAD unchanged
2505 #[test]
2506 fn recovery_stale_divider_is_cleared_before_fallback_recovery() {
2507 new_ocw_ext().execute_with(|| {
2508 register_block_hashes(0, 5);
2509
2510 set_block(2); TestForks::start(None, None, || {});
2511 set_block(3); TestForks::start(None, None, || {});
2512 set_block(4); TestForks::start(None, None, || {});
2513 assert_eq!(read_head(), Some(3));
2514
2515 // Capture the exact divider that parent_branch_unavailable will clear.
2516 let divider_hash = TestForks::get_divider(mock_hash(2))
2517 .expect("divider for mock_hash(2) must exist before corruption");
2518
2519 // routing resolves before corruption.
2520 assert!(resolve_branch(3).is_some(),
2521 "block 3 routing must be intact before corruption");
2522
2523 // Wipe the branch payload only.
2524 // block_key -> divider -> branch_key all remain intact.
2525 let b3 = resolve_branch(3).expect("block 3 must resolve");
2526 let root_key = branch_key(TAG, b3.genesis, &[]);
2527 StorageValueRef::persistent(&root_key).clear();
2528
2529 // routing now fails at the branch payload level.
2530 assert!(resolve_branch(3).is_none(),
2531 "routing must return None when branch payload is missing");
2532
2533 // start() again at N=4: block=3 <= head=3 -> sibling path
2534 // -> get_branch returns None -> parent_branch_unavailable fires
2535 // -> clears divider_hash -> recovery runs.
2536 set_block(4);
2537 let mut ran = false;
2538 TestForks::start(None, None, || { ran = true; });
2539
2540 // divider_hash must be absent: parent_branch_unavailable cleared it
2541 // before delegating to recovery.
2542 assert!(
2543 load_value::<[u8; 32]>(÷r_hash).is_none(),
2544 "stale divider must be cleared before recovery"
2545 );
2546
2547 assert!(ran, "ocw must run after stale-divider recovery");
2548
2549 // HEAD must not advance - sibling path does not update HEAD.
2550 assert_eq!(read_head(), Some(3));
2551 });
2552 }
2553
2554 // -------------------------------------------------------------------------
2555 // ````` TS 9 - RECOVERY: MULTI-BLOCK GAP WITH SHARED SYNTHETIC BRANCH `````
2556 // -------------------------------------------------------------------------
2557
2558 /// Behavior: the forward rebuild restores routing from recoverer+1 up to
2559 /// block (exclusive). Block 3 is the recoverer itself and is not restored,
2560 /// only block 4 is rebuilt, pointing to block 2 as its parent.
2561 ///
2562 /// Scenario:
2563 /// - Before: A(0) -> B(1) -> C(2) -> D(3) -> E(4) => HEAD=4
2564 /// - Corrupt: block_key for blocks 3 and 4 wiped
2565 ///
2566 /// Recovery walks back from recoverer=3, finds ancestor at block_hash(2).
2567 /// Forward rebuild: target=4 only (target starts at recoverer+1=4).
2568 /// block_key for block 3 is not restored.
2569 /// block_key for block 4 is restored (synthetic branch with parent=block2_branch).
2570 ///
2571 /// Result: F(5) resolved, HEAD=5
2572 #[test]
2573 fn recovery_rebuilds_multi_block_gap_with_shared_synthetic_branch() {
2574 new_ocw_ext().execute_with(|| {
2575 register_block_hashes(0, 7);
2576
2577 set_block(2);
2578 TestForks::start(None, None, || {});
2579 set_block(3);
2580 TestForks::start(None, None, || {});
2581 set_block(4);
2582 TestForks::start(None, None, || {});
2583 set_block(5);
2584 TestForks::start(None, None, || {});
2585 assert_eq!(read_head(), Some(4));
2586
2587 StorageValueRef::persistent(&block_key(TAG, mock_hash(3))).clear();
2588 StorageValueRef::persistent(&block_key(TAG, mock_hash(4))).clear();
2589
2590 assert!(resolve_branch(3).is_none(), "block 3 routing must be broken");
2591 assert!(resolve_branch(4).is_none(), "block 4 routing must be broken");
2592
2593 set_block(6);
2594 let mut ran = false;
2595 TestForks::start(None, None, || { ran = true; });
2596
2597 assert!(ran, "ocw must run after multi-block gap recovery");
2598 assert_eq!(read_head(), Some(5));
2599
2600 // Block 5 must resolve - it is the block resolved by this start() call.
2601 let b5 = resolve_branch(5).expect("block 5 routing must resolve");
2602 assert_eq!(b5.head, 5);
2603
2604 // Block 4 rebuilt by forward loop - parent links to block 2 (last intact ancestor).
2605 let b4 = resolve_branch(4).expect("block 4 routing must be rebuilt by forward recovery");
2606 let parent_key = b4.parent.expect("synthetic branch must carry parent linkage");
2607 let parent_branch = TestForks::get_branch(&parent_key)
2608 .expect("synthetic branch parent must be loadable");
2609 let b2 = resolve_branch(2).expect("block 2 must be intact");
2610 assert_eq!(parent_branch.genesis, b2.genesis,
2611 "synthetic branch parent must link to the last intact ancestor (block 2)");
2612
2613 // Block 3 is the recoverer itself - the forward loop starts at recoverer+1=4
2614 // and does not restore block 3's routing. It remains unresolvable.
2615 assert!(resolve_branch(3).is_none(),
2616 "block 3 routing is not restored by forward rebuild");
2617 });
2618 }
2619
2620 // -------------------------------------------------------------------------
2621 // ```````````````` TS 10 - RECOVERY: DECODE ERROR IN MUTATE ```````````````
2622 // -------------------------------------------------------------------------
2623
2624 /// Behavior: a garbage branch payload causes Branch::decode to fail inside
2625 /// the mutate closure. inherited_branch_decode_error -> parent_branch_unavailable
2626 /// -> stale divider cleared -> parent_branch_hash_unavailable rebuilds routing.
2627 ///
2628 /// Scenario:
2629 /// - Canonical: A(0) -> B(1) -> C(2) => HEAD=2
2630 /// - Corrupt: branch payload replaced with 0xDEADBEEF
2631 ///
2632 /// mutate -> Err(decode) -> inherited_branch_decode_error -> recovery
2633 /// Result: D(3) resolved, HEAD=3
2634 #[test]
2635 fn decode_error_in_mutate_triggers_recovery_and_ocw_still_runs() {
2636 new_ocw_ext().execute_with(|| {
2637 register_block_hashes(0, 5);
2638
2639 set_block(2);
2640 TestForks::start(None, None, || {});
2641 set_block(3);
2642 TestForks::start(None, None, || {});
2643 assert_eq!(read_head(), Some(2));
2644
2645 // Derive genesis from the routing chain, not by independent computation.
2646 let b2 = resolve_branch(2).expect("block 2 must resolve before corruption");
2647 let root_key = branch_key(TAG, b2.genesis, &[]);
2648 StorageValueRef::persistent(&root_key).set(&[0xDE, 0xAD, 0xBE, 0xEF]);
2649
2650 assert!(resolve_branch(2).is_none(),
2651 "routing must return None when payload is corrupt");
2652
2653 set_block(4);
2654 let mut ran = false;
2655 TestForks::start(None, None, || { ran = true; });
2656
2657 assert!(ran, "ocw must run after decode-error recovery");
2658 assert_eq!(read_head(), Some(3), "HEAD must advance to 3");
2659
2660 let b3 = resolve_branch(3).expect("block 3 routing must resolve after recovery");
2661 assert_eq!(b3.head, 3);
2662 });
2663 }
2664
2665 // -------------------------------------------------------------------------
2666 // `````````` TS 11 - CONCURRENT MODIFICATION / SIBLING PROMOTION ``````````
2667 //
2668 // StorageValueRef::mutate internally uses compare-and-set (CAS), so
2669 // ConcurrentModification only happens if some other write updates
2670 // storage between the read and write phase.
2671 //
2672 // In this test environment everything runs single-threaded, so we can't
2673 // realistically reproduce that race condition through start() itself.
2674 //
2675 // Instead, this test calls inherited_branch_mutation_conflict()
2676 // directly and verifies the same recovery path:
2677 //
2678 // - a sibling branch is created in the next available slot
2679 // - the sibling keeps a parent pointer to the original branch
2680 // - routing is rebuilt for the conflicted block
2681 // - HEAD remains consistent after recovery
2682 // -------------------------------------------------------------------------
2683
2684 /// Behavior: inherited_branch_mutation_conflict promotes the second writer
2685 /// into a new sibling branch. The sibling shares genesis with the original
2686 /// branch, carries parent=original_branch_key, and gets its own routing.
2687 ///
2688 /// Scenario:
2689 /// Canonical chain built to HEAD=2.
2690 /// Writer 1 already committed block 2 on the root branch (counter=[]).
2691 /// Writer 2 arrives late - simulated by calling the conflict handler
2692 /// directly with block=2 (the block both writers competed on).
2693 ///
2694 /// Before conflict handler:
2695 /// root counter=[] head=2 (writer 1 committed)
2696 ///
2697 /// After conflict handler:
2698 /// root counter=[] head=2 (unchanged)
2699 /// |
2700 /// +-- sibling counter=[0] head=2 parent=root_key (writer 2)
2701 #[test]
2702 fn concurrent_modification_promotes_conflict_writer_to_sibling_branch() {
2703 new_ocw_ext().execute_with(|| {
2704 register_block_hashes(0, 4);
2705
2706 set_block(2); TestForks::start(None, None, || {});
2707 set_block(3); TestForks::start(None, None, || {});
2708 assert_eq!(read_head(), Some(2));
2709
2710 // The conflict handler resolves parent = block_hash(block-1=1) = mock_hash(1).
2711 // Capture the root branch at that key before the conflict fires.
2712 let root_branch_hash = TestForks::get_branch_hash(mock_hash(1))
2713 .expect("root branch hash must exist");
2714
2715 // Verify the root branch is reachable and at the expected state.
2716 let root = TestForks::get_branch(&root_branch_hash)
2717 .expect("root branch must be loadable");
2718 assert!(root.counter.is_empty());
2719 assert_eq!(root.head, 2);
2720
2721 // Fire the conflict handler directly for block=2.
2722 let result = TestForks::inherited_branch_mutation_conflict(2, None, None);
2723 assert!(result.is_ok(), "conflict handler must succeed");
2724
2725 assert_eq!(read_head(), Some(2), "HEAD must be 2 after conflict resolution");
2726
2727 // Routing for block 2 must now point to the sibling branch.
2728 let sibling = TestForks::get_block_branch(mock_hash(2))
2729 .expect("block 2 routing must resolve to the sibling branch");
2730
2731 assert_eq!(sibling.counter, vec![0u32]);
2732 assert_eq!(sibling.head, 2);
2733
2734 // Sibling must point to the root branch as its structural parent.
2735 let sibling_parent_key = sibling.parent
2736 .expect("sibling must carry a parent key");
2737 assert_eq!(sibling_parent_key, root_branch_hash,
2738 "sibling parent must be the root branch (writer 1's branch)");
2739
2740 // Root branch must be unchanged - writer 1's commit is preserved.
2741 let root_after = TestForks::get_branch(&root_branch_hash)
2742 .expect("root branch must still exist");
2743 assert_eq!(root_after.counter, root.counter);
2744 assert_eq!(root_after.head, root.head);
2745 assert_eq!(root_after.genesis, sibling.genesis);
2746 });
2747 }
2748
2749 // -------------------------------------------------------------------------
2750 // `````````````````````` TS 12 - MAX FORKS EXHAUSTION `````````````````````
2751 // -------------------------------------------------------------------------
2752
2753 /// Behavior: when all counter slots [0..=MAX_FORKS] under the fork's
2754 /// genesis are occupied, the slot search fails, max_forks() returns Err,
2755 /// the loop breaks, and the OCW closure is not called.
2756 ///
2757 /// Scenario:
2758 /// - Canonical: A(0) -> B(1) -> C(2) -> D(3) => HEAD=3
2759 /// - Fork: competing hashes for blocks 2 and 3
2760 /// all sibling slots pre-filled under fork genesis
2761 ///
2762 /// Result: OCW not called, HEAD stays at 3
2763 #[test]
2764 fn max_forks_exhaustion_prevents_sibling_creation_and_ocw() {
2765 new_ocw_ext().execute_with(|| {
2766 register_block_hashes(0, 5);
2767
2768 set_block(2);
2769 TestForks::start(None, None, || {});
2770 set_block(3);
2771 TestForks::start(None, None, || {});
2772 set_block(4);
2773 TestForks::start(None, None, || {});
2774 assert_eq!(read_head(), Some(3));
2775
2776 // Fork genesis derived from grand_parent = block_hash(3-2=1)
2777 let genesis_fork = recovery_genesis(1);
2778 let root_key = branch_key(TAG, genesis_fork, &[]);
2779
2780 store_encoded(&root_key, &Branch::<Test, TestScope> {
2781 parent: None,
2782 head: 2,
2783 scope: TestScope::default(),
2784 genesis: genesis_fork,
2785 counter: vec![],
2786 });
2787
2788 // Fill every sibling slot so no free slot exists.
2789 for i in 0u32..=TestForks::MAX_FORKS {
2790 let sibling_key = branch_key(TAG, genesis_fork, &[i]);
2791 store_encoded(&sibling_key, &Branch::<Test, TestScope> {
2792 parent: Some(root_key),
2793 head: 2,
2794 scope: TestScope::default(),
2795 genesis: genesis_fork,
2796 counter: vec![i],
2797 });
2798 }
2799
2800 let fork_hash_2 = BlakeTwo256::hash(b"max_fork_block_2");
2801 let fork_hash_3 = BlakeTwo256::hash(b"max_fork_block_3");
2802 frame_system::BlockHash::<Test>::insert(2, fork_hash_2);
2803 frame_system::BlockHash::<Test>::insert(3, fork_hash_3);
2804
2805 // Wire routing so the fork is reachable via block_key(TAG, fork_hash_2).
2806 let dh = divider_key(TAG, fork_hash_2, root_key);
2807 store_encoded(&dh, &root_key);
2808 store_encoded(&block_key(TAG, fork_hash_2), &dh);
2809
2810 set_block(4);
2811 let mut ran = false;
2812 TestForks::start(None, None, || { ran = true; });
2813
2814 assert!(!ran, "ocw must NOT run when MAX_FORKS is exhausted");
2815 assert_eq!(read_head(), Some(3), "HEAD must stay at 3");
2816 assert!(
2817 branch_by_lineage(genesis_fork, &[TestForks::MAX_FORKS + 1]).is_none(),
2818 "no branch beyond MAX_FORKS must be created"
2819 );
2820 });
2821 }
2822
2823 // -------------------------------------------------------------------------
2824 // ``````````` TS 13 - RECOVERY: SYNTHETIC BRANCH PARENT LINKAGE ```````````
2825 // -------------------------------------------------------------------------
2826
2827 /// Behavior: every synthetic branch created during forward rebuild must
2828 /// carry a parent pointer to the last intact ancestor branch, not None.
2829 ///
2830 /// Scenario:
2831 /// - Canonical: A(0) -> B(1) -> C(2) -> D(3) => HEAD=3
2832 /// - Corrupt: block_key for block 3 wiped
2833 ///
2834 /// Rebuilt block 3 branch: parent = block_2_branch_key
2835 #[test]
2836 fn recovery_synthetic_branch_preserves_parent_linkage() {
2837 new_ocw_ext().execute_with(|| {
2838 register_block_hashes(0, 5);
2839
2840 set_block(2);
2841 TestForks::start(None, None, || {});
2842 set_block(3);
2843 TestForks::start(None, None, || {});
2844 set_block(4);
2845 TestForks::start(None, None, || {});
2846
2847 // Capture block 2 branch hash before wiping block 3.
2848 let b2_branch_hash = TestForks::get_branch_hash(mock_hash(2))
2849 .expect("block 2 branch hash must exist");
2850
2851 StorageValueRef::persistent(&block_key(TAG, mock_hash(3))).clear();
2852
2853 set_block(5);
2854 TestForks::start(None, None, || {});
2855
2856 let b3 = resolve_branch(3).expect("block 3 routing must be rebuilt");
2857
2858 let parent_key = b3.parent
2859 .expect("rebuilt synthetic branch must carry a parent key");
2860 assert_eq!(parent_key, b2_branch_hash,
2861 "synthetic branch parent must point to block 2's branch");
2862
2863 // Navigating to the parent must load block 2's branch.
2864 let parent_branch = TestForks::get_branch(&parent_key)
2865 .expect("parent branch must be loadable");
2866 let b2 = resolve_branch(2).expect("block 2 must still be intact");
2867 assert_eq!(parent_branch.genesis, b2.genesis);
2868 });
2869 }
2870
2871 // -------------------------------------------------------------------------
2872 // ``````````````````` TS 14 - REPEATED START IDEMPOTENCY ``````````````````
2873 // -------------------------------------------------------------------------
2874
2875 /// Behavior: calling start() twice at the same block does not corrupt HEAD,
2876 /// duplicate branches, or alter existing routing.
2877 ///
2878 /// Scenario: start(N=3) called twice in succession.
2879 #[test]
2880 fn repeated_start_at_same_block_is_idempotent() {
2881 new_ocw_ext().execute_with(|| {
2882 register_block_hashes(0, 4);
2883
2884 set_block(2);
2885 TestForks::start(None, None, || {});
2886 set_block(3);
2887 TestForks::start(None, None, || {});
2888
2889 let head_first = read_head();
2890 let b2_first = resolve_branch(2).expect("block 2 must resolve");
2891
2892 set_block(3);
2893 TestForks::start(None, None, || {});
2894
2895 assert_eq!(read_head(), head_first, "HEAD must not change after repeated start");
2896
2897 let b2_second = resolve_branch(2).expect("block 2 must still resolve");
2898 assert_eq!(b2_first.genesis, b2_second.genesis,
2899 "repeated start must not alter branch genesis");
2900 assert_eq!(b2_first.head, b2_second.head,
2901 "repeated start must not alter branch head");
2902 });
2903 }
2904
2905 // -------------------------------------------------------------------------
2906 // ````````````````````` TS 15 - FORK GRAPH NAVIGATION `````````````````````
2907 // -------------------------------------------------------------------------
2908 // Verifies all ForkAction variants against a graph with a canonical root
2909 // and a fork branch created by injecting competing hashes at block 3.
2910 //
2911 // Graph after setup:
2912 //
2913 // [canonical]
2914 // root counter=[] head=3 parent=None
2915 // ^
2916 // | fork_root.parent points here (set by forward rebuild recovery)
2917 // |
2918 // [fork]
2919 // fork_root counter=[] head=2 parent=Some(canonical_root_key)
2920 // |
2921 // +-- sibling counter=[0] head=3 parent=Some(fork_root_key)
2922 #[test]
2923 fn fork_action_handler_navigation_after_graph_built() {
2924 new_ocw_ext().execute_with(|| {
2925 register_block_hashes(0, 6);
2926
2927 set_block(2);
2928 TestForks::start(None, None, || {});
2929 set_block(3);
2930 TestForks::start(None, None, || {});
2931 set_block(4);
2932 TestForks::start(None, None, || {});
2933
2934 // Create sibling fork at block 3 by injecting competing hashes.
2935 let fork_hash_3 = BlakeTwo256::hash(b"nav_fork_3");
2936 let fork_hash_2 = BlakeTwo256::hash(b"nav_fork_2");
2937 frame_system::BlockHash::<Test>::insert(3, fork_hash_3);
2938 frame_system::BlockHash::<Test>::insert(2, fork_hash_2);
2939 set_block(4);
2940 TestForks::start(None, None, || {});
2941
2942 // Load branches via routing chains
2943 frame_system::BlockHash::<Test>::insert(3, mock_hash(3));
2944 frame_system::BlockHash::<Test>::insert(2, mock_hash(2));
2945
2946 let root = resolve_branch(3)
2947 .expect("canonical root must resolve via block 3 routing");
2948 let sibling = TestForks::get_block_branch(fork_hash_3)
2949 .expect("sibling must resolve via fork_hash_3 routing chain");
2950 let fork_root_key = sibling.parent
2951 .expect("sibling must carry a parent key");
2952 let fork_root = TestForks::get_branch(&fork_root_key)
2953 .expect("fork_root must be loadable via sibling.parent");
2954
2955 // Graph invariants
2956 assert_eq!(root.head, 3);
2957 assert!(root.parent.is_none());
2958 assert_eq!(fork_root.head, 2);
2959 assert!(fork_root.counter.is_empty());
2960 assert_eq!(sibling.head, 3);
2961 assert_eq!(sibling.counter, vec![0u32]);
2962
2963 // ----------------------- MoveToParentBranch ----------------------
2964
2965 // sibling[0] -> fork_root[]: parent pointer leads to fork_root.
2966 let p = TestForks::transition(&sibling, ForkAction::MoveToParentBranch)
2967 .expect("MoveToParentBranch from sibling must succeed");
2968 assert!(p.counter.is_empty(), );
2969 assert_eq!(p.head, fork_root.head);
2970
2971 // fork_root.parent was set by recovery to the canonical root.
2972 let fork_root_parent = TestForks::transition(&fork_root, ForkAction::MoveToParentBranch)
2973 .expect("MoveToParentBranch on fork_root must succeed");
2974
2975 // The parent must be the canonical root - same genesis, same counter shape.
2976 assert_eq!(fork_root_parent.genesis, root.genesis);
2977 assert!(fork_root_parent.counter.is_empty());
2978 assert_eq!(fork_root_parent.head, root.head);
2979
2980 // ------------------- MoveToParentBranchBack(n) -------------------
2981
2982 // n=0: no-op, returns self unchanged.
2983 let b = TestForks::transition(&sibling, ForkAction::MoveToParentBranchBack(0))
2984 .expect("MoveToParentBranchBack(0) must return self");
2985 assert_eq!(b.counter, sibling.counter);
2986 assert_eq!(b.head, sibling.head);
2987
2988 // n=1: sibling -> fork_root.
2989 let b = TestForks::transition(&sibling, ForkAction::MoveToParentBranchBack(1))
2990 .expect("MoveToParentBranchBack(1) must succeed");
2991 assert!(b.counter.is_empty());
2992
2993 // n=2: sibling -> fork_root -> canonical_root (fork_root has parent set by recovery)
2994 let b = TestForks::transition(&sibling, ForkAction::MoveToParentBranchBack(2))
2995 .expect("MoveToParentBranchBack(2) must succeed - sibling has 2 levels of ancestry");
2996 assert_eq!(
2997 b.genesis, root.genesis,
2998 "Back(2) from sibling must reach the canonical root"
2999 );
3000
3001 // n=3: exceeds actual ancestry depth -> None
3002 assert!(
3003 TestForks::transition(&sibling, ForkAction::MoveToParentBranchBack(3)).is_none(),
3004 "MoveToParentBranchBack(3) exceeds ancestry depth -> None"
3005 );
3006
3007 // -------------------- MoveToChildBranch(index) -------------------
3008
3009 let child = TestForks::transition(&fork_root, ForkAction::MoveToChildBranch(0))
3010 .expect("MoveToChildBranch(0) from fork_root must succeed");
3011 assert_eq!(child.counter, vec![0u32]);
3012
3013 assert!(
3014 TestForks::transition(&fork_root, ForkAction::MoveToChildBranch(1)).is_none(),
3015 "MoveToChildBranch(1) where no child exists must return None"
3016 );
3017
3018 // sibling has no children -> None.
3019 assert!(
3020 TestForks::transition(&sibling, ForkAction::MoveToChildBranch(0)).is_none(),
3021 "MoveToChildBranch(0) from a leaf branch must return None"
3022 );
3023
3024 // --------------------- MoveToNextChildBranch ---------------------
3025
3026 // fork_root -> sibling (child index 0).
3027 let child = TestForks::transition(&fork_root, ForkAction::MoveToNextChildBranch)
3028 .expect("MoveToNextChildBranch from fork_root must succeed");
3029 assert_eq!(child.counter, vec![0u32]);
3030
3031 // sibling has no children -> None.
3032 assert!(
3033 TestForks::transition(&sibling, ForkAction::MoveToNextChildBranch).is_none(),
3034 "MoveToNextChildBranch from leaf must return None"
3035 );
3036
3037 // ------------------- MoveToSiblingBranch(index) ------------------
3038
3039 // fork_root (counter=[]) requesting index=0 -> sibling[0] (exists).
3040 let sib = TestForks::transition(&fork_root, ForkAction::MoveToSiblingBranch(0))
3041 .expect("MoveToSiblingBranch(0) from root must reach sibling[0]");
3042 assert_eq!(sib.counter, vec![0u32]);
3043
3044 // sibling (counter=[0]) requesting index=0 -> same as self -> None.
3045 assert!(
3046 TestForks::transition(&sibling, ForkAction::MoveToSiblingBranch(0)).is_none(),
3047 "MoveToSiblingBranch(same index) must return None"
3048 );
3049
3050 // fork_root requesting index=1 -> slot [1] does not exist -> None.
3051 assert!(
3052 TestForks::transition(&fork_root, ForkAction::MoveToSiblingBranch(1)).is_none(),
3053 "MoveToSiblingBranch to a non-existent slot must return None"
3054 );
3055
3056 // -------------------- MoveToNextSiblingBranch --------------------
3057
3058 // fork_root (counter=[]): last=None -> next_index=0 -> sibling[0] exists.
3059 let arrived = TestForks::transition(&fork_root, ForkAction::MoveToNextSiblingBranch)
3060 .expect("MoveToNextSiblingBranch from fork_root must succeed");
3061 assert_eq!(arrived.counter, vec![0u32]);
3062
3063 // sibling (counter=[0]): last=0 -> next_index=1 -> slot [1] does not exist -> None.
3064 assert!(
3065 TestForks::transition(&sibling, ForkAction::MoveToNextSiblingBranch).is_none(),
3066 "MoveToNextSiblingBranch when next slot is empty must return None"
3067 );
3068
3069 // ------------------ MoveToPreviousSiblingBranch ------------------
3070
3071 // sibling (counter=[0]): last=0 -> immediately returns None (no index -1).
3072 assert!(
3073 TestForks::transition(&sibling, ForkAction::MoveToPreviousSiblingBranch).is_none(),
3074 "MoveToPreviousSiblingBranch at index 0 must return None"
3075 );
3076
3077 // fork_root (counter=[]): counter.last() = None -> None.
3078 assert!(
3079 TestForks::transition(&fork_root, ForkAction::MoveToPreviousSiblingBranch).is_none(),
3080 "MoveToPreviousSiblingBranch on a root (counter=[]) must return None"
3081 );
3082
3083 // ------------------------ MoveToRootBranch ------------------------
3084
3085 // sibling -> fork_root -> canonical_root (parent=None) -> stops at canonical_root.
3086 let root_b = TestForks::transition(&sibling, ForkAction::MoveToRootBranch)
3087 .expect("MoveToRootBranch from sibling must succeed");
3088 assert!(root_b.parent.is_none());
3089 assert_eq!(root_b.genesis, root.genesis);
3090
3091 // fork_root also has a parent (canonical root), so MoveToRootBranch walks
3092 // one more level and lands on the canonical root too.
3093 let root_from_fork = TestForks::transition(&fork_root, ForkAction::MoveToRootBranch)
3094 .expect("MoveToRootBranch from fork_root must succeed");
3095 assert!(root_from_fork.parent.is_none());
3096 assert_eq!(root_from_fork.genesis, root.genesis);
3097 });
3098 }
3099
3100 // -------------------------------------------------------------------------
3101 // ```````````````````` TS 16 - SCOPE HANDLER OPERATIONS ```````````````````
3102 // -------------------------------------------------------------------------
3103
3104 /// Behavior: gen_scope_item_key produces a stable, deterministic 32-byte key.
3105 /// The same item always maps to the same key; different items produce different keys.
3106 ///
3107 /// Scenario: two distinct items encoded as byte slices.
3108 #[test]
3109 fn scope_handler_gen_scope_item_key_is_deterministic_and_distinct() {
3110 let item_a: Vec<u8> = b"key_a".to_vec();
3111 let item_b: Vec<u8> = b"key_b".to_vec();
3112
3113 let k1 = TestForks::gen_scope_item_key(&item_a);
3114 let k2 = TestForks::gen_scope_item_key(&item_a);
3115 let k3 = TestForks::gen_scope_item_key(&item_b);
3116
3117 // Deterministic
3118 assert_eq!(k1, k2);
3119
3120 // Distinct
3121 assert_ne!(k1, k3);
3122 }
3123
3124 /// Behavior: scope_item_exists returns Err(forks_not_enabled) when called
3125 /// before ForksHandler::start has built the fork graph.
3126 ///
3127 /// Scenario: no start() call, direct scope_item_exists at block 2.
3128 #[test]
3129 fn scope_handler_scope_item_exists_returns_err_when_fork_graph_not_initialized() {
3130 new_ocw_ext().execute_with(|| {
3131 register_block_hashes(0, 4);
3132 set_block(2);
3133
3134 let key = TestForks::gen_scope_item_key(&b"test_item".to_vec());
3135 let result = TestForks::scope_item_exists(&key, None, None);
3136
3137 assert_eq!(result, Err(DispatchError::Other("forks-not-enabled")));
3138 });
3139 }
3140
3141 /// Behavior: scope_item_exists returns Ok(false) for a key not yet added
3142 /// after the fork graph is initialized.
3143 ///
3144 /// Scenario: start() runs at block 2, key queried without prior add_to_scope.
3145 #[test]
3146 fn scope_handler_scope_item_exists_returns_false_for_absent_key() {
3147 new_ocw_ext().execute_with(|| {
3148 register_block_hashes(0, 4);
3149 set_block(2);
3150 TestForks::start(None, None, || {});
3151 set_block(3);
3152 TestForks::start(None, None, || {});
3153
3154 let key = TestForks::gen_scope_item_key(&b"absent".to_vec());
3155 let result = TestForks::scope_item_exists(&key, None, None);
3156
3157 assert_eq!(result, Ok(false));
3158 });
3159 }
3160
3161 /// Behavior: add_to_scope writes the item into local_keys of the current branch.
3162 /// scope_item_exists returns Ok(true) for the key on the same branch.
3163 ///
3164 /// Scenario: start() at block 2, add_to_scope at block 3, scope_item_exists at block 3.
3165 #[test]
3166 fn scope_handler_add_to_scope_makes_key_visible_in_local_scope() {
3167 new_ocw_ext().execute_with(|| {
3168 register_block_hashes(0, 5);
3169 set_block(2);
3170 TestForks::start(None, None, || {});
3171 set_block(3);
3172 TestForks::start(None, None, || {});
3173
3174 // add_to_scope writes into branch at block 2 (block - 1 = 2).
3175 let item = b"active_key".to_vec();
3176 let key = TestForks::add_to_scope(item.clone(), None, None).unwrap();
3177
3178 // scope_item_exists at block 3 reads branch at block 2.
3179 let exists = TestForks::scope_item_exists(&key, None, None).unwrap();
3180
3181 assert!(exists);
3182 });
3183 }
3184
3185 /// Behavior: add_to_scope returns Err(forks_not_enabled) when the fork graph
3186 /// has not been initialized.
3187 ///
3188 /// Scenario: no start() call before add_to_scope.
3189 #[test]
3190 fn scope_handler_add_to_scope_returns_err_when_fork_graph_not_initialized() {
3191 new_ocw_ext().execute_with(|| {
3192 register_block_hashes(0, 4);
3193 set_block(2);
3194
3195 let result = TestForks::add_to_scope(b"item".to_vec(), None, None);
3196
3197 assert_eq!(result, Err(DispatchError::Other("forks-not-enabled")));
3198 });
3199 }
3200
3201 /// Behavior: a key added to local_keys propagates into inherited_keys of the
3202 /// next branch via accrete(), making it visible on the child branch.
3203 ///
3204 /// Scenario: add_to_scope at block 3, advance to block 4 (new branch created),
3205 /// scope_item_exists at block 4 finds key in inherited scope.
3206 #[test]
3207 fn scope_handler_local_key_propagates_to_inherited_on_next_branch() {
3208 new_ocw_ext().execute_with(|| {
3209 register_block_hashes(0, 6);
3210
3211 set_block(2);
3212 TestForks::start(None, None, || {});
3213 set_block(3);
3214 TestForks::start(None, None, || {});
3215
3216 let item = b"propagated_key".to_vec();
3217 let key = TestForks::add_to_scope(item.clone(), None, None).unwrap();
3218
3219 // Advance to block 4 - accrete() promotes local_keys into inherited_keys.
3220 set_block(4);
3221 TestForks::start(None, None, || {});
3222
3223 let exists = TestForks::scope_item_exists(&key, None, None).unwrap();
3224
3225 assert!(exists);
3226 });
3227 }
3228
3229 /// Behavior: remove_from_scope removes a key from local_keys on the current branch.
3230 /// scope_item_exists returns Ok(false) after removal.
3231 ///
3232 /// Scenario: add_to_scope then remove_from_scope on the same branch.
3233 #[test]
3234 fn scope_handler_remove_from_scope_removes_key_from_local_scope() {
3235 new_ocw_ext().execute_with(|| {
3236 register_block_hashes(0, 5);
3237 set_block(2);
3238 TestForks::start(None, None, || {});
3239 set_block(3);
3240 TestForks::start(None, None, || {});
3241
3242 let item = b"removable_key".to_vec();
3243 let key = TestForks::add_to_scope(item.clone(), None, None).unwrap();
3244
3245 // Key exists before removal.
3246 assert!(TestForks::scope_item_exists(&key, None, None).unwrap());
3247
3248 TestForks::remove_from_scope(&key, None, None).unwrap();
3249
3250 // Key is absent after removal.
3251 let exists = TestForks::scope_item_exists(&key, None, None).unwrap();
3252
3253 assert!(!exists);
3254 });
3255 }
3256
3257 /// Behavior: remove_from_scope removes a key from inherited_keys when it
3258 /// was promoted from a previous branch.
3259 ///
3260 /// Scenario: add_to_scope at block 3, advance to block 4 (key becomes inherited),
3261 /// remove_from_scope at block 4.
3262 #[test]
3263 fn scope_handler_remove_from_scope_removes_key_from_inherited_scope() {
3264 new_ocw_ext().execute_with(|| {
3265 register_block_hashes(0, 6);
3266
3267 set_block(2);
3268 TestForks::start(None, None, || {});
3269 set_block(3);
3270 TestForks::start(None, None, || {});
3271
3272 let item = b"inherited_key".to_vec();
3273 let key = TestForks::add_to_scope(item.clone(), None, None).unwrap();
3274
3275 set_block(4);
3276 TestForks::start(None, None, || {});
3277
3278 assert!(TestForks::scope_item_exists(&key, None, None).unwrap());
3279
3280 TestForks::remove_from_scope(&key, None, None).unwrap();
3281
3282 let exists = TestForks::scope_item_exists(&key, None, None).unwrap();
3283
3284 assert!(!exists);
3285 });
3286 }
3287
3288 /// Behavior: remove_from_scope is a no-op for a key that does not exist
3289 /// in either local or inherited scope. Returns Ok(()).
3290 ///
3291 /// Scenario: remove_from_scope called with a key that was never added.
3292 #[test]
3293 fn scope_handler_remove_from_scope_is_noop_for_absent_key() {
3294 new_ocw_ext().execute_with(|| {
3295 register_block_hashes(0, 4);
3296 set_block(2);
3297 TestForks::start(None, None, || {});
3298 set_block(3);
3299 TestForks::start(None, None, || {});
3300
3301 let key = TestForks::gen_scope_item_key(&b"never_added".to_vec());
3302 let result = TestForks::remove_from_scope(&key, None, None);
3303
3304 assert_eq!(result, Ok(()));
3305 });
3306 }
3307
3308 /// Behavior: remove_from_scope returns Err(forks_not_enabled) when the
3309 /// fork graph has not been initialized.
3310 ///
3311 /// Scenario: no start() call before remove_from_scope.
3312 #[test]
3313 fn scope_handler_remove_from_scope_returns_err_when_fork_graph_not_initialized() {
3314 new_ocw_ext().execute_with(|| {
3315 register_block_hashes(0, 4);
3316 set_block(2);
3317
3318 let key = TestForks::gen_scope_item_key(&b"item".to_vec());
3319 let result = TestForks::remove_from_scope(&key, None, None);
3320
3321 assert_eq!(result, Err(DispatchError::Other("forks-not-enabled")));
3322 });
3323 }
3324}