@@ -2846,6 +2846,27 @@ impl NakamotoChainState {
2846
2846
Self :: get_block_header_nakamoto ( chainstate_conn. sqlite ( ) , & block_id)
2847
2847
}
2848
2848
2849
+ /// Get the first canonical block header in a vector of height-ordered candidates
2850
+ fn get_highest_canonical_block_header_from_candidates (
2851
+ sort_db : & SortitionDB ,
2852
+ candidates : Vec < StacksHeaderInfo > ,
2853
+ ) -> Result < Option < StacksHeaderInfo > , ChainstateError > {
2854
+ let canonical_sortition_handle = sort_db. index_handle_at_tip ( ) ;
2855
+ for candidate in candidates. into_iter ( ) {
2856
+ let Some ( ref candidate_ch) = candidate. burn_view else {
2857
+ // this is an epoch 2.x header, no burn view to check
2858
+ return Ok ( Some ( candidate) ) ;
2859
+ } ;
2860
+ let in_canonical_fork = canonical_sortition_handle. processed_block ( & candidate_ch) ?;
2861
+ if in_canonical_fork {
2862
+ return Ok ( Some ( candidate) ) ;
2863
+ }
2864
+ }
2865
+
2866
+ // did not find any blocks in candidates
2867
+ Ok ( None )
2868
+ }
2869
+
2849
2870
/// Get the highest block in the given tenure on a given fork.
2850
2871
/// Only works on Nakamoto blocks.
2851
2872
/// TODO: unit test
@@ -2879,20 +2900,7 @@ impl NakamotoChainState {
2879
2900
tenure_id,
2880
2901
) ?;
2881
2902
2882
- let canonical_sortition_handle = sort_db. index_handle_at_tip ( ) ;
2883
- for candidate in candidates. into_iter ( ) {
2884
- let Some ( ref candidate_ch) = candidate. burn_view else {
2885
- // this is an epoch 2.x header, no burn view to check
2886
- return Ok ( Some ( candidate) ) ;
2887
- } ;
2888
- let in_canonical_fork = canonical_sortition_handle. processed_block ( & candidate_ch) ?;
2889
- if in_canonical_fork {
2890
- return Ok ( Some ( candidate) ) ;
2891
- }
2892
- }
2893
-
2894
- // did not find any blocks in the tenure
2895
- Ok ( None )
2903
+ Self :: get_highest_canonical_block_header_from_candidates ( sort_db, candidates)
2896
2904
}
2897
2905
2898
2906
/// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
@@ -2931,6 +2939,116 @@ impl NakamotoChainState {
2931
2939
Ok ( Vec :: from_iter ( epoch2_x) )
2932
2940
}
2933
2941
2942
+ /// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
2943
+ /// tenure.
2944
+ ///
2945
+ /// Get the highest block in a given tenure (identified by burnchain block height) with a canonical
2946
+ /// burn_view (i.e., burn_view on the canonical sortition fork). This covers only Nakamoto blocks.
2947
+ /// Epoch2 blocks will not be checked.
2948
+ pub fn find_highest_known_block_header_in_tenure_by_block_height (
2949
+ chainstate : & StacksChainState ,
2950
+ sort_db : & SortitionDB ,
2951
+ tenure_height : u64 ,
2952
+ ) -> Result < Option < StacksHeaderInfo > , ChainstateError > {
2953
+ let chainstate_db_conn = chainstate. db ( ) ;
2954
+
2955
+ let candidates =
2956
+ Self :: get_highest_known_block_header_in_tenure_by_block_height_at_each_burnview (
2957
+ chainstate_db_conn,
2958
+ tenure_height,
2959
+ ) ?;
2960
+
2961
+ Self :: get_highest_canonical_block_header_from_candidates ( sort_db, candidates)
2962
+ }
2963
+
2964
+ /// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
2965
+ /// tenure.
2966
+ ///
2967
+ /// Get the highest block in a given tenure (identified by burnchain block hash) with a canonical
2968
+ /// burn_view (i.e., burn_view on the canonical sortition fork). This covers only Nakamoto blocks.
2969
+ /// Epoch2 blocks will not be checked.
2970
+ pub fn find_highest_known_block_header_in_tenure_by_block_hash (
2971
+ chainstate : & StacksChainState ,
2972
+ sort_db : & SortitionDB ,
2973
+ tenure_block_hash : & BurnchainHeaderHash ,
2974
+ ) -> Result < Option < StacksHeaderInfo > , ChainstateError > {
2975
+ let chainstate_db_conn = chainstate. db ( ) ;
2976
+
2977
+ let candidates =
2978
+ Self :: get_highest_known_block_header_in_tenure_by_block_hash_at_each_burnview (
2979
+ chainstate_db_conn,
2980
+ tenure_block_hash,
2981
+ ) ?;
2982
+
2983
+ Self :: get_highest_canonical_block_header_from_candidates ( sort_db, candidates)
2984
+ }
2985
+
2986
+ /// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
2987
+ /// tenure.
2988
+ ///
2989
+ /// Get the highest blocks in a given tenure (identified by burnchain block height) at each burn view
2990
+ /// active in that tenure. If there are ties at a given burn view, they will both be returned
2991
+ fn get_highest_known_block_header_in_tenure_by_block_height_at_each_burnview (
2992
+ db : & Connection ,
2993
+ tenure_height : u64 ,
2994
+ ) -> Result < Vec < StacksHeaderInfo > , ChainstateError > {
2995
+ // see if we have a nakamoto block in this tenure
2996
+ let qry = "
2997
+ SELECT h.*
2998
+ FROM nakamoto_block_headers h
2999
+ JOIN (
3000
+ SELECT burn_view, MAX(block_height) AS max_height
3001
+ FROM nakamoto_block_headers
3002
+ WHERE burn_header_height = ?1
3003
+ GROUP BY burn_view
3004
+ ) maxed
3005
+ ON h.burn_view = maxed.burn_view
3006
+ AND h.block_height = maxed.max_height
3007
+ WHERE h.burn_header_height = ?1
3008
+ ORDER BY h.block_height DESC, h.timestamp
3009
+ " ;
3010
+ let args = params ! [ tenure_height] ;
3011
+ let out = query_rows ( db, qry, args) ?;
3012
+ if !out. is_empty ( ) {
3013
+ return Ok ( out) ;
3014
+ }
3015
+
3016
+ Err ( ChainstateError :: NoSuchBlockError )
3017
+ }
3018
+
3019
+ /// DO NOT USE IN CONSENSUS CODE. Different nodes can have different blocks for the same
3020
+ /// tenure.
3021
+ ///
3022
+ /// Get the highest blocks in a given tenure (identified by burnchain block hash) at each burn view
3023
+ /// active in that tenure. If there are ties at a given burn view, they will both be returned
3024
+ fn get_highest_known_block_header_in_tenure_by_block_hash_at_each_burnview (
3025
+ db : & Connection ,
3026
+ tenure_block_hash : & BurnchainHeaderHash ,
3027
+ ) -> Result < Vec < StacksHeaderInfo > , ChainstateError > {
3028
+ // see if we have a nakamoto block in this tenure
3029
+ let qry = "
3030
+ SELECT h.*
3031
+ FROM nakamoto_block_headers h
3032
+ JOIN (
3033
+ SELECT burn_view, MAX(block_height) AS max_height
3034
+ FROM nakamoto_block_headers
3035
+ WHERE burn_header_hash = ?1
3036
+ GROUP BY burn_view
3037
+ ) maxed
3038
+ ON h.burn_view = maxed.burn_view
3039
+ AND h.block_height = maxed.max_height
3040
+ WHERE h.burn_header_hash = ?1
3041
+ ORDER BY h.block_height DESC, h.timestamp
3042
+ " ;
3043
+ let args = params ! [ tenure_block_hash] ;
3044
+ let out = query_rows ( db, qry, args) ?;
3045
+ if !out. is_empty ( ) {
3046
+ return Ok ( out) ;
3047
+ }
3048
+
3049
+ Err ( ChainstateError :: NoSuchBlockError )
3050
+ }
3051
+
2934
3052
/// Get the VRF proof for a Stacks block.
2935
3053
/// For Nakamoto blocks, this is the VRF proof contained in the coinbase of the tenure-start
2936
3054
/// block of the given tenure identified by the consensus hash.
0 commit comments