Skip to content

Commit

Permalink
Check for trailing entries
Browse files Browse the repository at this point in the history
  • Loading branch information
jstarry committed Oct 20, 2019
1 parent 361b884 commit 22cfa26
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 3 deletions.
35 changes: 34 additions & 1 deletion core/src/replay_stage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -770,8 +770,16 @@ impl ReplayStage {
return Err(BlockError::InvalidTickHashCount);
}

if bank.tick_height() + entries.tick_count() > bank.max_tick_height() {
let next_bank_tick_height = bank.tick_height() + entries.tick_count();
let max_bank_tick_height = bank.max_tick_height();
if next_bank_tick_height > max_bank_tick_height {
return Err(BlockError::InvalidTickCount);
} else if next_bank_tick_height == max_bank_tick_height {
if let Some(last_entry) = entries.last() {
if !last_entry.is_tick() {
return Err(BlockError::TrailingEntry);
}
}
}

let now = Instant::now();
Expand Down Expand Up @@ -1073,6 +1081,31 @@ mod test {
}
}

#[test]
fn test_dead_fork_trailing_entry() {
let keypair1 = Keypair::new();
let keypair2 = Keypair::new();
let res = check_dead_fork(|bank| {
let blockhash = bank.last_blockhash();
let slot = bank.slot();
let hashes_per_tick = bank.hashes_per_tick().unwrap_or(0);
let mut entries =
entry::create_ticks(bank.ticks_per_slot(), hashes_per_tick, blockhash.clone());
let last_entry_hash = entries.last().unwrap().hash;
let tx =
system_transaction::transfer_now(&keypair1, &keypair2.pubkey(), 2, last_entry_hash);
let trailing_entry = entry::next_entry(&last_entry_hash, 1, vec![tx]);
entries.push(trailing_entry);
entries_to_test_shreds(entries, slot, slot.saturating_sub(1), false)
});

if let Err(Error::BlockError(block_error)) = res {
assert_eq!(block_error, BlockError::TrailingEntry);
} else {
assert!(false);
}
}

#[test]
fn test_dead_fork_entry_deserialize_failure() {
// Insert entry that causes deserialization failure
Expand Down
9 changes: 9 additions & 0 deletions ledger/src/block_error.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
#[derive(Debug, PartialEq)]
pub enum BlockError {
/// Block entries hashes must all be valid
InvalidEntryHash,

/// Blocks can not have extra ticks or missing ticks
InvalidTickCount,

/// All ticks must contain the same number of hashes within a block
InvalidTickHashCount,

/// Blocks must end in a tick entry, trailing transaction entries are not allowed to guarantee
/// that each block has the same number of hashes
TrailingEntry,
}
59 changes: 57 additions & 2 deletions ledger/src/blocktree_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,9 @@ fn verify_and_process_slot_entries(
bank.slot()
);
return Err(BlockError::InvalidTickCount.into());
} else if !entries.last().unwrap().is_tick() {
warn!("Slot: {} did not end with a tick entry", bank.slot());
return Err(BlockError::TrailingEntry.into());
}

if let Some(hashes_per_tick) = bank.hashes_per_tick() {
Expand Down Expand Up @@ -558,7 +561,9 @@ pub mod tests {
};
assert_eq!(
process_blocktree(&genesis_block, &blocktree, None, opts).err(),
Some(BlocktreeProcessorError::InvalidTickHashCount)
Some(BlocktreeProcessorError::InvalidBlock(
BlockError::InvalidTickHashCount
)),
);
}

Expand Down Expand Up @@ -596,7 +601,57 @@ pub mod tests {
};
assert_eq!(
process_blocktree(&genesis_block, &blocktree, None, opts).err(),
Some(BlocktreeProcessorError::InvalidSlotTickCount),
Some(BlocktreeProcessorError::InvalidBlock(
BlockError::InvalidTickCount
)),
);
}

#[test]
fn test_process_blocktree_with_slot_with_trailing_entry() {
solana_logger::setup();

let GenesisBlockInfo { genesis_block, .. } = create_genesis_block(10_000);
let ticks_per_slot = genesis_block.ticks_per_slot;

let (ledger_path, blockhash) = create_new_tmp_ledger!(&genesis_block);
let blocktree = Blocktree::open(&ledger_path).unwrap();

let mut entries = create_ticks(ticks_per_slot, 0, blockhash);
let trailing_entry = {
let keypair1 = Keypair::new();
let keypair2 = Keypair::new();
let tx = system_transaction::transfer_now(&keypair1, &keypair2.pubkey(), 1, blockhash);
next_entry(&blockhash, 1, vec![tx])
};
entries.push(trailing_entry);

// Tricks blocktree into writing the trailing entry by lying that there is one more tick
// per slot.
let parent_slot = 0;
let slot = 1;
blocktree
.write_entries(
slot,
0,
0,
ticks_per_slot + 1,
Some(parent_slot),
true,
&Arc::new(Keypair::new()),
entries,
)
.expect("Expected to write shredded entries to blocktree");

let opts = ProcessOptions {
verify_ledger: true,
..ProcessOptions::default()
};
assert_eq!(
process_blocktree(&genesis_block, &blocktree, None, opts).err(),
Some(BlocktreeProcessorError::InvalidBlock(
BlockError::TrailingEntry
)),
);
}

Expand Down

0 comments on commit 22cfa26

Please sign in to comment.