2
2
3
3
//---------------------------------------------------------------------------------------------------- Import
4
4
use std:: {
5
+ cmp:: min,
5
6
collections:: { HashMap , HashSet } ,
6
7
sync:: Arc ,
7
8
} ;
8
9
9
10
use rayon:: {
10
- iter:: { IntoParallelIterator , ParallelIterator , Either } ,
11
+ iter:: { Either , IntoParallelIterator , ParallelIterator } ,
11
12
prelude:: * ,
12
13
ThreadPool ,
13
14
} ;
14
15
use thread_local:: ThreadLocal ;
15
16
16
- use cuprate_database:: { ConcreteEnv , DatabaseRo , Env , EnvInner , RuntimeError } ;
17
+ use cuprate_database:: { ConcreteEnv , DatabaseIter , DatabaseRo , Env , EnvInner , RuntimeError } ;
17
18
use cuprate_database_service:: { init_thread_pool, DatabaseReadService , ReaderThreads } ;
18
19
use cuprate_helper:: map:: combine_low_high_bits_to_u128;
19
20
use cuprate_types:: {
20
21
blockchain:: { BlockchainReadRequest , BlockchainResponse } ,
21
- BlockCompleteEntry , Chain , ChainId , ExtendedBlockHeader , OutputOnChain ,
22
+ Chain , ChainId , ExtendedBlockHeader , OutputOnChain ,
22
23
} ;
23
24
24
- use crate :: ops:: block:: get_block_complete_entry;
25
25
use crate :: {
26
26
ops:: {
27
27
alt_block:: {
28
28
get_alt_block, get_alt_block_extended_header_from_height, get_alt_block_hash,
29
29
get_alt_chain_history_ranges,
30
30
} ,
31
31
block:: {
32
- block_exists, get_block_extended_header_from_height, get_block_height, get_block_info,
32
+ block_exists, get_block_blob_with_tx_indexes, get_block_complete_entry,
33
+ get_block_extended_header_from_height, get_block_height, get_block_info,
33
34
} ,
34
- blockchain:: { cumulative_generated_coins, top_block_height} ,
35
+ blockchain:: { cumulative_generated_coins, find_split_point , top_block_height} ,
35
36
key_image:: key_image_exists,
36
37
output:: id_to_output_on_chain,
37
38
} ,
38
39
service:: {
39
40
free:: { compact_history_genesis_not_included, compact_history_index_to_height_offset} ,
40
41
types:: { BlockchainReadHandle , ResponseResult } ,
41
42
} ,
42
- tables:: { AltBlockHeights , BlockHeights , BlockInfos , OpenTables , Tables } ,
43
+ tables:: { AltBlockHeights , BlockHeights , BlockInfos , OpenTables , Tables , TablesIter } ,
43
44
types:: {
44
45
AltBlockHeight , Amount , AmountIndex , BlockHash , BlockHeight , KeyImage , PreRctOutputId ,
45
46
} ,
@@ -107,6 +108,7 @@ fn map_request(
107
108
R :: NumberOutputsWithAmount ( vec) => number_outputs_with_amount ( env, vec) ,
108
109
R :: KeyImagesSpent ( set) => key_images_spent ( env, set) ,
109
110
R :: CompactChainHistory => compact_chain_history ( env) ,
111
+ R :: NextChainEntry ( block_hashes, amount) => next_chain_entry ( env, & block_hashes, amount) ,
110
112
R :: FindFirstUnknown ( block_ids) => find_first_unknown ( env, & block_ids) ,
111
113
R :: AltBlocksInChain ( chain_id) => alt_blocks_in_chain ( env, chain_id) ,
112
114
}
@@ -552,6 +554,76 @@ fn compact_chain_history(env: &ConcreteEnv) -> ResponseResult {
552
554
} )
553
555
}
554
556
557
+ /// [`BlockchainReadRequest::NextChainEntry`]
558
+ ///
559
+ /// # Invariant
560
+ /// `block_ids` must be sorted in reverse chronological block order, or else
561
+ /// the returned result is unspecified and meaningless, as this function
562
+ /// performs a binary search.
563
+ fn next_chain_entry (
564
+ env : & ConcreteEnv ,
565
+ block_ids : & [ BlockHash ] ,
566
+ next_entry_size : usize ,
567
+ ) -> ResponseResult {
568
+ // Single-threaded, no `ThreadLocal` required.
569
+ let env_inner = env. env_inner ( ) ;
570
+ let tx_ro = env_inner. tx_ro ( ) ?;
571
+
572
+ let tables = env_inner. open_tables ( & tx_ro) ?;
573
+ let table_block_heights = tables. block_heights ( ) ;
574
+ let table_block_infos = tables. block_infos_iter ( ) ;
575
+
576
+ let idx = find_split_point ( block_ids, false , table_block_heights) ?;
577
+
578
+ // This will happen if we have a different genesis block.
579
+ if idx == block_ids. len ( ) {
580
+ return Ok ( BlockchainResponse :: NextChainEntry {
581
+ start_height : 0 ,
582
+ chain_height : 0 ,
583
+ block_ids : vec ! [ ] ,
584
+ block_weights : vec ! [ ] ,
585
+ cumulative_difficulty : 0 ,
586
+ first_block_blob : None ,
587
+ } ) ;
588
+ }
589
+
590
+ // The returned chain entry must overlap with one of the blocks we were told about.
591
+ let first_known_block_hash = block_ids[ idx] ;
592
+ let first_known_height = table_block_heights. get ( & first_known_block_hash) ?;
593
+
594
+ let chain_height = crate :: ops:: blockchain:: chain_height ( table_block_heights) ?;
595
+ let last_height_in_chain_entry = min ( first_known_height + next_entry_size, chain_height) ;
596
+
597
+ let ( block_ids, block_weights) = table_block_infos
598
+ . get_range ( first_known_height..last_height_in_chain_entry) ?
599
+ . map ( |block_info| {
600
+ let block_info = block_info?;
601
+
602
+ Ok ( ( block_info. block_hash , block_info. weight ) )
603
+ } )
604
+ . collect :: < Result < ( Vec < _ > , Vec < _ > ) , RuntimeError > > ( ) ?;
605
+
606
+ let top_block_info = table_block_infos. get ( & ( chain_height - 1 ) ) ?;
607
+
608
+ let first_block_blob = if block_ids. len ( ) >= 2 {
609
+ Some ( get_block_blob_with_tx_indexes ( & ( first_known_height + 1 ) , & tables) ?. 0 )
610
+ } else {
611
+ None
612
+ } ;
613
+
614
+ Ok ( BlockchainResponse :: NextChainEntry {
615
+ start_height : first_known_height,
616
+ chain_height,
617
+ block_ids,
618
+ block_weights,
619
+ cumulative_difficulty : combine_low_high_bits_to_u128 (
620
+ top_block_info. cumulative_difficulty_low ,
621
+ top_block_info. cumulative_difficulty_high ,
622
+ ) ,
623
+ first_block_blob,
624
+ } )
625
+ }
626
+
555
627
/// [`BlockchainReadRequest::FindFirstUnknown`]
556
628
///
557
629
/// # Invariant
@@ -564,24 +636,7 @@ fn find_first_unknown(env: &ConcreteEnv, block_ids: &[BlockHash]) -> ResponseRes
564
636
565
637
let table_block_heights = env_inner. open_db_ro :: < BlockHeights > ( & tx_ro) ?;
566
638
567
- let mut err = None ;
568
-
569
- // Do a binary search to find the first unknown block in the batch.
570
- let idx =
571
- block_ids. partition_point (
572
- |block_id| match block_exists ( block_id, & table_block_heights) {
573
- Ok ( exists) => exists,
574
- Err ( e) => {
575
- err. get_or_insert ( e) ;
576
- // if this happens the search is scrapped, just return `false` back.
577
- false
578
- }
579
- } ,
580
- ) ;
581
-
582
- if let Some ( e) = err {
583
- return Err ( e) ;
584
- }
639
+ let idx = find_split_point ( block_ids, true , & table_block_heights) ?;
585
640
586
641
Ok ( if idx == block_ids. len ( ) {
587
642
BlockchainResponse :: FindFirstUnknown ( None )
0 commit comments