@@ -57,9 +57,9 @@ impl<T: RelationsStoreReader, U: ReachabilityService> RelationsStoreReader for R
5757 hashes
5858 . iter ( )
5959 . copied ( )
60- // TODO(relaxed): consider removing this filtering altogether - in the context this is currently called
61- // blocks should not be inserted if they do not fulfill this condition
62- . filter ( |h| self . reachability_service . is_dag_ancestor_of_result ( self . root , * h ) . optional ( ) . unwrap ( ) . unwrap_or ( false ) )
60+ . filter ( |h| {
61+ self . reachability_service . is_dag_ancestor_of_result ( self . root , * h ) . optional ( ) . unwrap ( ) . unwrap_or ( false )
62+ } )
6363 . collect_vec ( ) ,
6464 )
6565 } )
@@ -199,9 +199,6 @@ impl PruningProofManager {
199199 }
200200
201201 fn calc_all_level_proof_stores ( & self , pp_header : & HeaderWithBlockLevel , temp_db : Arc < DB > ) -> MultiLevelProofContext {
202- // TODO: Uncomment line and send as argument to find_sufficiently_deep_level_root
203- // once full fix to minimize proof sizes comes
204- // let current_dag_level = self.find_current_dag_level(&pp_header.header);
205202 let mut level_proof_stores_vec: Vec < Option < LevelProofContext > > = vec ! [ None ; ( self . max_block_level + 1 ) . into( ) ] ;
206203 for level in ( 0 ..=self . max_block_level ) . rev ( ) {
207204 let level_usize = level as usize ;
@@ -229,75 +226,6 @@ impl PruningProofManager {
229226 MultiLevelProofContext { transient_ghostdag_stores, transient_relations_stores, selected_tip_by_level, roots_by_level }
230227 }
231228
232- /// Builds and returns a store of parents-children relations at a given level
233- /// to facilitate forward traversal of a snippet of the Dag at that level.
234- /// This relation store contains all reachable blocks
235- /// that belong in the future of root and the past of tip.
236- /// Non reachable blocks are filtered out as they are not conceptually a part of the Dag at that level
237- /// but simply remain in node storage for other reasons.
238- fn populate_relation_store_at_level (
239- & self ,
240- temp_db : Arc < DB > ,
241- tip : Hash ,
242- root : Hash ,
243- level : BlockLevel ,
244- try_number : u8 ,
245- ) -> DbRelationsStore {
246- //TODO(relaxed): currently we rebuild the relation store in its entirety for each try.
247- // A more efficient implementation could instead extend the store constructed in the previous try
248- //TODO: Remove sanity checks when convinced code is stable and correct
249- let mut queue = VecDeque :: new ( ) ;
250- let mut visited = BlockHashSet :: new ( ) ;
251- queue. push_back ( tip) ;
252- let cache_policy = CachePolicy :: Count ( 2 * self . pruning_proof_m as usize ) ;
253- let mut level_relation_store = DbRelationsStore :: new_temp ( temp_db. clone ( ) , level, try_number, cache_policy, cache_policy) ;
254-
255- level_relation_store. insert ( ORIGIN , BlockHashes :: new ( vec ! [ ] ) ) . unwrap ( ) ;
256-
257- // sanity check
258- assert ! ( self . reachability_service. is_dag_ancestor_of( root, tip) ) ;
259- while let Some ( hash) = queue. pop_front ( ) {
260- if !visited. insert ( hash) {
261- continue ;
262- }
263- if hash == ORIGIN {
264- continue ;
265- }
266- let header = self . headers_store . get_header ( hash) . unwrap ( ) ;
267- let parents = Arc :: new (
268- self . parents_manager
269- . parents_at_level ( & header, level)
270- . iter ( )
271- . copied ( )
272- . filter ( |& p| self . reachability_service . is_dag_ancestor_of_result ( root, p) . optional ( ) . unwrap ( ) . unwrap_or ( false ) )
273- . collect :: < Vec < _ > > ( )
274- . push_if_empty ( ORIGIN ) ,
275- ) ;
276- // sanity check
277- if hash == root {
278- assert_eq ! ( parents. len( ) , 1 ) ;
279- assert_eq ! ( parents[ 0 ] , ORIGIN ) ;
280- }
281- // Write parents to the relations store
282- level_relation_store. insert ( hash, parents. clone ( ) ) . unwrap ( ) ;
283-
284- // Enqueue parents to fill full upper chain
285- for & p in parents. iter ( ) {
286- queue. push_back ( p) ;
287- }
288- }
289- //sanity checks
290- for hash in visited {
291- assert ! ( level_relation_store. has( hash) . unwrap( ) ) ;
292- }
293- let children_read = level_relation_store. get_children ( ORIGIN ) . unwrap ( ) ;
294- let origin_children = children_read. read ( ) ;
295- assert ! ( origin_children. len( ) == 1 ) ;
296- assert ! ( origin_children. contains( & root) ) ;
297-
298- level_relation_store
299- }
300-
301229 /// Given a current hash, count the blocks in its future
302230 /// Do this by:
303231 /// 1. Getting the largest child in terms of future size (this ensures that the rest of the iteration is minimized since most of the future is covered)
@@ -507,129 +435,6 @@ impl PruningProofManager {
507435 panic ! ( "Failed to find sufficient root for level {level} after exhausting all known headers." ) ;
508436 }
509437
510- /// Find a sufficient root at a given level by going through the headers store and looking
511- /// for a deep enough level block
512- /// For each root candidate, fill in the ghostdag data to see if it actually is deep enough.
513- /// If the root is deep enough, it will satisfy these conditions
514- /// 1. block at depth 2m at this level ∈ Future(root)
515- /// 2. block at depth m at the next level ∈ Future(root)
516- ///
517- /// Returns: the filled ghostdag store from root to tip, the selected tip and the root
518- fn find_sufficiently_deep_level_root (
519- & self ,
520- pp_header : & HeaderWithBlockLevel ,
521- level : BlockLevel ,
522- required_block : Option < Hash > ,
523- temp_db : Arc < DB > ,
524- ) -> PruningProofManagerInternalResult < LevelProofContext > {
525- // Step 1: Determine which selected tip to use
526- let selected_tip = if pp_header. block_level >= level {
527- pp_header. header . hash
528- } else {
529- self . find_approximate_selected_parent_header_at_level ( & pp_header. header , level) ?. hash
530- } ;
531-
532- let cache_policy = CachePolicy :: Count ( 2 * self . pruning_proof_m as usize ) ;
533- let required_level_depth = 2 * self . pruning_proof_m ;
534-
535- // We only have the headers store (which has level 0 blue_scores) to assemble the proof data from.
536- // We need to look deeper at higher levels (2x deeper every level) to find 2M (plus margin) blocks at that level
537- // TODO: uncomment when the full fix to minimize proof sizes comes, and add current_dag_level as an argument
538- // let mut required_base_level_depth = self.estimated_blue_depth_at_level_0(
539- // level,
540- // required_level_depth + 100, // We take a safety margin
541- // current_dag_level,
542- // );
543- // NOTE: Starting from required_level_depth (a much lower starting point than normal) will typically require O(N) iterations
544- // for level L + N where L is the current dag level. This is fine since the steps per iteration are still exponential
545- // and so we will complete each level in not much more than N iterations per level.
546- // We start here anyway so we can try to minimize the proof size when the current dag level goes down significantly.
547- let mut required_base_level_depth = required_level_depth + 100 ;
548-
549- let mut is_last_level_header;
550- let mut tries = 0 ;
551-
552- let block_at_depth_m_at_next_level = required_block. unwrap_or ( selected_tip) ;
553-
554- loop {
555- // Step 2 - Find a deep enough root candidate
556- let block_at_depth_2m = match self . level_block_at_base_depth ( level, selected_tip, required_base_level_depth) {
557- Ok ( ( header, is_last_header) ) => {
558- is_last_level_header = is_last_header;
559- header
560- }
561- Err ( e) => return Err ( e) ,
562- } ;
563-
564- let root = if self . reachability_service . is_dag_ancestor_of ( block_at_depth_2m, block_at_depth_m_at_next_level) {
565- block_at_depth_2m
566- } else if self . reachability_service . is_dag_ancestor_of ( block_at_depth_m_at_next_level, block_at_depth_2m) {
567- block_at_depth_m_at_next_level
568- } else {
569- // find common ancestor of block_at_depth_m_at_next_level and block_at_depth_2m in chain of block_at_depth_m_at_next_level
570- let mut common_ancestor = self . headers_store . get_header ( block_at_depth_m_at_next_level) . unwrap ( ) ;
571- while !self . reachability_service . is_dag_ancestor_of ( common_ancestor. hash , block_at_depth_2m) {
572- common_ancestor = match self . find_approximate_selected_parent_header_at_level ( & common_ancestor, level) {
573- Ok ( header) => header,
574- // Try to give this last header a chance at being root
575- Err ( PruningProofManagerInternalError :: NotEnoughHeadersToBuildProof ( _) ) => break ,
576- Err ( e) => return Err ( e) ,
577- } ;
578- }
579-
580- common_ancestor. hash
581- } ;
582-
583- // Step 3 - Fill the ghostdag data from root to tip.
584- //
585- // First, derive the relevant relations down to the candidate root.
586- let transient_relation_store =
587- Arc :: new ( RwLock :: new ( self . populate_relation_store_at_level ( temp_db. clone ( ) , selected_tip, root, level, tries) ) ) ;
588-
589- let transient_ghostdag_store =
590- Arc :: new ( DbGhostdagStore :: new_temp ( temp_db. clone ( ) , level, cache_policy, cache_policy, tries) ) ;
591- let has_required_block = self . fill_level_proof_ghostdag_data (
592- root,
593- pp_header. header . hash ,
594- & transient_ghostdag_store,
595- Some ( block_at_depth_m_at_next_level) ,
596- level,
597- & transient_relation_store,
598- self . ghostdag_k ,
599- ) ;
600-
601- // Step 4 - Check if we actually have enough depth.
602- // Need to ensure this does the same 2M+1 depth that block_at_depth does
603- if has_required_block
604- && ( root == self . genesis_hash
605- || transient_ghostdag_store. get_blue_score ( selected_tip) . unwrap ( ) >= required_level_depth)
606- {
607- if tries >= 1 {
608- debug ! ( "Found sufficient root for level {level} after {tries} tries." ) ;
609- }
610- break Ok ( ( transient_ghostdag_store, transient_relation_store, selected_tip, root) ) ;
611- }
612-
613- tries += 1 ;
614- if is_last_level_header {
615- if has_required_block {
616- // Normally this scenario doesn't occur when syncing with nodes that already have the safety margin change in place.
617- // However, when syncing with an older node version that doesn't have a safety margin for the proof, it's possible to
618- // try to find 2500 depth worth of headers at a level, but the proof only contains about 2000 headers. To be able to sync
619- // with such an older node. As long as we found the required block, we can still proceed.
620- debug ! ( "Failed to find sufficient root for level {level} after {tries} tries. Headers below the current depth of {required_base_level_depth} are already pruned. Required block found so trying anyway." ) ;
621- break Ok ( ( transient_ghostdag_store, transient_relation_store, selected_tip, root) ) ;
622- } else {
623- panic ! ( "Failed to find sufficient root for level {level} after {tries} tries. Headers below the current depth of {required_base_level_depth} are already pruned" ) ;
624- }
625- }
626-
627- // If we don't have enough depth now, we need to look deeper
628- required_base_level_depth = ( required_base_level_depth as f64 * 1.1 ) as u64 ;
629- debug ! ( "Failed to find sufficient root for level {level} after {tries} tries. Retrying again to find with depth {required_base_level_depth}" ) ;
630- }
631- }
632-
633438 /// BFS forward iterates from root until selected tip, ignoring blocks in the antipast of selected_tip.
634439 /// For each block along the way, insert that hash into the ghostdag_store
635440 /// If we have a required_block to find, this will return true if that block was found along the way
@@ -701,33 +506,6 @@ impl PruningProofManager {
701506 has_required_block
702507 }
703508
704- // The "current dag level" is the level right before the level whose parents are
705- // not the same as our header's direct parents
706- //
707- // Find the current DAG level by going through all the parents at each level,
708- // starting from the bottom level and see which is the first level that has
709- // parents that are NOT our current pp_header's direct parents.
710- fn find_current_dag_level ( & self , pp_header : & Header ) -> BlockLevel {
711- let direct_parents = BlockHashSet :: from_iter ( pp_header. direct_parents ( ) . iter ( ) . copied ( ) ) ;
712- pp_header
713- . parents_by_level
714- . expanded_iter ( )
715- . enumerate ( )
716- . skip ( 1 ) // skip checking direct parents
717- . find_map ( |( level, parents) | {
718- if BlockHashSet :: from_iter ( parents. iter ( ) . copied ( ) ) == direct_parents {
719- None
720- } else {
721- Some ( ( level - 1 ) as BlockLevel )
722- }
723- } )
724- . unwrap_or ( self . max_block_level )
725- }
726-
727- fn estimated_blue_depth_at_level_0 ( & self , level : BlockLevel , level_depth : u64 , current_dag_level : BlockLevel ) -> u64 {
728- level_depth. checked_shl ( level. saturating_sub ( current_dag_level) as u32 ) . unwrap_or ( level_depth)
729- }
730-
731509 /// selected parent at level = the parent of the header at the level
732510 /// with the highest blue_work
733511 fn find_approximate_selected_parent_header_at_level (
@@ -752,73 +530,6 @@ impl PruningProofManager {
752530 Ok ( self . headers_store . get_header ( sp. hash ) . expect ( "unwrapped above" ) )
753531 }
754532
755- /// Finds a block on a given level that is at base_depth deep from it.
756- /// Also returns if the block was the last one in the level
757- /// base_depth = the blue score depth at level 0
758- fn level_block_at_base_depth (
759- & self ,
760- level : BlockLevel ,
761- high : Hash ,
762- base_depth : u64 ,
763- ) -> PruningProofManagerInternalResult < ( Hash , bool ) > {
764- let high_header = self
765- . headers_store
766- . get_header ( high)
767- . map_err ( |err| PruningProofManagerInternalError :: BlockAtDepth ( format ! ( "high: {high}, depth: {base_depth}, {err}" ) ) ) ?;
768- let high_header_score = high_header. blue_score ;
769- let mut current_header = high_header;
770-
771- let mut is_last_header = false ;
772- // The current block is considered seen
773- // This counts all the blocks we've seen, including the anticone of the current header
774- // The anticone size of the root/current header can be removed from this to get the estimated number of blocks
775- // fully in the set of Future(root) ∩ Past(high)
776- let mut seen = 1 ;
777-
778- let mut anticone = Vec :: new ( ) ;
779- let depth_2m = 2 * self . pruning_proof_m as usize ;
780-
781- // The block is of sufficient depth if:
782- // 1. It has the required base depth AND
783- // 2. We've seen 2M+1 blocks in it's future (See other references to 2M+1 in this file)
784- while current_header. blue_score + base_depth >= high_header_score || seen - anticone. len ( ) <= depth_2m {
785- if current_header. direct_parents ( ) . is_empty ( ) {
786- // Reached genesis
787- break ;
788- }
789-
790- let mut known_parents = self
791- . parents_manager
792- . parents_at_level ( & current_header, level)
793- . iter ( )
794- . copied ( )
795- . filter ( |& p| self . reachability_service . has_reachability_data ( p) )
796- . collect_vec ( ) ;
797-
798- seen += known_parents. len ( ) ;
799-
800- current_header = match self . find_approximate_selected_parent_header_at_level ( & current_header, level) {
801- Ok ( header) => header,
802- Err ( PruningProofManagerInternalError :: NotEnoughHeadersToBuildProof ( _) ) => {
803- // We want to give this root a shot if all its past is pruned
804- is_last_header = true ;
805- break ;
806- }
807- Err ( e) => return Err ( e) ,
808- } ;
809-
810- // Maintain the anticone by adding all the known parents of the previous header
811- // and then removing all of those that are not ancestors of the current header
812- anticone. append ( & mut known_parents) ;
813- anticone = anticone
814- . iter ( )
815- . filter ( |& b| !self . reachability_service . is_dag_ancestor_of ( current_header. hash , * b) )
816- . copied ( )
817- . collect_vec ( ) ;
818- }
819- Ok ( ( current_header. hash , is_last_header) )
820- }
821-
822533 /// Copy of `block_at_depth` which returns the full chain up to depth. Temporarily used for assertion purposes.
823534 fn chain_up_to_depth (
824535 & self ,
0 commit comments