Skip to content

Commit

Permalink
Fix get_first_available_block for genesis; also make get_blocks(_with…
Browse files Browse the repository at this point in the history
…_limit) consistent (solana-labs#24760)

* Handle genesis more appropriately in get_first_available_block

* Add unit test

* Make get_blocks starting-slots consistent with other methods
  • Loading branch information
Tyera Eulberg authored and jeffwashington committed Jun 29, 2022
1 parent 5da7503 commit 1d56045
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 11 deletions.
54 changes: 49 additions & 5 deletions ledger/src/blockstore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1915,11 +1915,17 @@ impl Blockstore {

/// The first complete block that is available in the Blockstore ledger
pub fn get_first_available_block(&self) -> Result<Slot> {
let mut root_iterator = self.rooted_slot_iterator(self.lowest_slot())?;
// The block at root-index 0 cannot be complete, because it is missing its parent
// blockhash. A parent blockhash must be calculated from the entries of the previous block.
// Therefore, the first available complete block is that at root-index 1.
Ok(root_iterator.nth(1).unwrap_or_default())
let mut root_iterator = self.rooted_slot_iterator(self.lowest_slot_with_genesis())?;
let first_root = root_iterator.next().unwrap_or_default();
// If the first root is slot 0, it is genesis. Genesis is always complete, so it is correct
// to return it as first-available.
if first_root == 0 {
return Ok(first_root);
}
// Otherwise, the block at root-index 0 cannot ever be complete, because it is missing its
// parent blockhash. A parent blockhash must be calculated from the entries of the previous
// block. Therefore, the first available complete block is that at root-index 1.
Ok(root_iterator.next().unwrap_or_default())
}

pub fn get_rooted_block(
Expand Down Expand Up @@ -3145,6 +3151,19 @@ impl Blockstore {
self.last_root()
}

fn lowest_slot_with_genesis(&self) -> Slot {
for (slot, meta) in self
.slot_meta_iterator(0)
.expect("unable to iterate over meta")
{
if meta.received > 0 {
return slot;
}
}
// This means blockstore is empty, should never get here aside from right at boot.
self.last_root()
}

pub fn lowest_cleanup_slot(&self) -> Slot {
*self.lowest_cleanup_slot.read().unwrap()
}
Expand Down Expand Up @@ -6294,6 +6313,31 @@ pub mod tests {
assert!(blockstore.get_data_shred(1, 0).unwrap().is_some());
}

#[test]
fn test_get_first_available_block() {
let mint_total = 1_000_000_000_000;
let GenesisConfigInfo { genesis_config, .. } = create_genesis_config(mint_total);
let (ledger_path, _blockhash) = create_new_tmp_ledger_auto_delete!(&genesis_config);
let blockstore = Blockstore::open(ledger_path.path()).unwrap();
assert_eq!(blockstore.get_first_available_block().unwrap(), 0);
assert_eq!(blockstore.lowest_slot_with_genesis(), 0);
assert_eq!(blockstore.lowest_slot(), 0);
for slot in 1..4 {
let entries = make_slot_entries_with_transactions(100);
let shreds = entries_to_test_shreds(&entries, slot, slot - 1, true, 0);
blockstore.insert_shreds(shreds, None, false).unwrap();
blockstore.set_roots(vec![slot].iter()).unwrap();
}
assert_eq!(blockstore.get_first_available_block().unwrap(), 0);
assert_eq!(blockstore.lowest_slot_with_genesis(), 0);
assert_eq!(blockstore.lowest_slot(), 1);

blockstore.purge_slots(0, 1, PurgeType::CompactionFilter);
assert_eq!(blockstore.get_first_available_block().unwrap(), 3);
assert_eq!(blockstore.lowest_slot_with_genesis(), 2);
assert_eq!(blockstore.lowest_slot(), 2);
}

#[test]
fn test_get_rooted_block() {
let slot = 10;
Expand Down
19 changes: 13 additions & 6 deletions rpc/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1124,7 +1124,10 @@ impl JsonRpcRequestProcessor {
)));
}

let lowest_blockstore_slot = self.blockstore.lowest_slot();
let lowest_blockstore_slot = self
.blockstore
.get_first_available_block()
.unwrap_or_default();
if start_slot < lowest_blockstore_slot {
// If the starting slot is lower than what's available in blockstore assume the entire
// [start_slot..end_slot] can be fetched from BigTable. This range should not ever run
Expand Down Expand Up @@ -1188,7 +1191,10 @@ impl JsonRpcRequestProcessor {
)));
}

let lowest_blockstore_slot = self.blockstore.lowest_slot();
let lowest_blockstore_slot = self
.blockstore
.get_first_available_block()
.unwrap_or_default();

if start_slot < lowest_blockstore_slot {
// If the starting slot is lower than what's available in blockstore assume the entire
Expand Down Expand Up @@ -6677,6 +6683,7 @@ pub mod tests {
#[test]
fn test_get_blocks() {
let rpc = RpcHandler::start();
let _ = rpc.create_test_transactions_and_populate_blockstore();
rpc.add_roots_to_blockstore(vec![0, 1, 3, 4, 8]);
rpc.block_commitment_cache
.write()
Expand All @@ -6685,19 +6692,19 @@ pub mod tests {

let request = create_test_request("getBlocks", Some(json!([0u64])));
let result: Vec<Slot> = parse_success_result(rpc.handle_request_sync(request));
assert_eq!(result, vec![1, 3, 4, 8]);
assert_eq!(result, vec![0, 1, 3, 4, 8]);

let request = create_test_request("getBlocks", Some(json!([2u64])));
let result: Vec<Slot> = parse_success_result(rpc.handle_request_sync(request));
assert_eq!(result, vec![3, 4, 8]);

let request = create_test_request("getBlocks", Some(json!([0u64, 4u64])));
let result: Vec<Slot> = parse_success_result(rpc.handle_request_sync(request));
assert_eq!(result, vec![1, 3, 4]);
assert_eq!(result, vec![0, 1, 3, 4]);

let request = create_test_request("getBlocks", Some(json!([0u64, 7u64])));
let result: Vec<Slot> = parse_success_result(rpc.handle_request_sync(request));
assert_eq!(result, vec![1, 3, 4]);
assert_eq!(result, vec![0, 1, 3, 4]);

let request = create_test_request("getBlocks", Some(json!([9u64, 11u64])));
let result: Vec<Slot> = parse_success_result(rpc.handle_request_sync(request));
Expand All @@ -6713,7 +6720,7 @@ pub mod tests {
Some(json!([0u64, MAX_GET_CONFIRMED_BLOCKS_RANGE])),
);
let result: Vec<Slot> = parse_success_result(rpc.handle_request_sync(request));
assert_eq!(result, vec![1, 3, 4, 8]);
assert_eq!(result, vec![0, 1, 3, 4, 8]);

let request = create_test_request(
"getBlocks",
Expand Down

0 comments on commit 1d56045

Please sign in to comment.