Fix get_first_available_block for genesis; also make get_blocks(_with_limit) consistent (#24760)

* Handle genesis more appropriately in get_first_available_block

* Add unit test

* Make get_blocks starting-slots consistent with other methods
This commit is contained in:
Tyera Eulberg 2022-04-28 02:36:19 -04:00 committed by GitHub
parent 8be726aa67
commit b6a18e0e87
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 62 additions and 11 deletions

View File

@ -1930,11 +1930,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(
@ -3160,6 +3166,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()
}
@ -6309,6 +6328,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;

View File

@ -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
@ -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
@ -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()
@ -6685,7 +6692,7 @@ 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));
@ -6693,11 +6700,11 @@ pub mod tests {
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));
@ -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",