Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion core/src/block_creation_loop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use {
bank::{Bank, NewBankOptions},
bank_forks::BankForks,
block_component_processor::BlockComponentProcessor,
validated_block_footer::ValidatedBlockFooter,
},
solana_version::version,
solana_votor::{common::block_timeout, event::LeaderWindowInfo},
Expand Down Expand Up @@ -464,13 +465,15 @@ fn record_and_complete_block(
let working_bank = w_poh_recorder.working_bank().unwrap();
let footer = produce_block_footer(working_bank.bank.clone_without_scheduler());

let footer = ValidatedBlockFooter::new_unchecked_for_block_producer(footer);

BlockComponentProcessor::update_bank_with_footer(
working_bank.bank.clone_without_scheduler(),
&footer,
);

drop(bank);
w_poh_recorder.tick_alpenglow(max_tick_height, footer);
w_poh_recorder.tick_alpenglow(max_tick_height, footer.into());

Ok(())
}
Expand Down
50 changes: 12 additions & 38 deletions runtime/src/block_component_processor.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use {
crate::bank::Bank,
crate::{
bank::Bank,
validated_block_footer::{ValidatedBlockFooter, ValidatedBlockFooterError},
},
solana_clock::{Slot, DEFAULT_MS_PER_SLOT},
solana_entry::block_component::{
BlockFooterV1, BlockMarkerV1, VersionedBlockFooter, VersionedBlockHeader,
VersionedBlockMarker,
BlockMarkerV1, VersionedBlockFooter, VersionedBlockHeader, VersionedBlockMarker,
},
solana_votor_messages::migration::MigrationStatus,
std::sync::Arc,
Expand All @@ -22,8 +24,8 @@ pub enum BlockComponentProcessorError {
MultipleBlockHeaders,
#[error("BlockComponent detected pre-migration")]
BlockComponentPreMigration,
#[error("Nanosecond clock out of bounds")]
NanosecondClockOutOfBounds,
#[error("invalid block footer")]
InvalidBlockFooter(#[from] ValidatedBlockFooterError),
}

#[derive(Default)]
Expand Down Expand Up @@ -119,9 +121,9 @@ impl BlockComponentProcessor {
let footer = match footer {
VersionedBlockFooter::V1(footer) | VersionedBlockFooter::Current(footer) => footer,
};

Self::enforce_nanosecond_clock_bounds(bank.clone(), parent_bank.clone(), footer)?;
Self::update_bank_with_footer(bank, footer);
let validated_block_footer =
ValidatedBlockFooter::try_new(&parent_bank, &bank, footer.clone())?;
Self::update_bank_with_footer(bank, &validated_block_footer);

self.has_footer = true;
Ok(())
Expand All @@ -139,34 +141,6 @@ impl BlockComponentProcessor {
Ok(())
}

fn enforce_nanosecond_clock_bounds(
bank: Arc<Bank>,
parent_bank: Arc<Bank>,
footer: &BlockFooterV1,
) -> Result<(), BlockComponentProcessorError> {
// Get parent time from nanosecond clock account
// If nanosecond clock hasn't been populated, don't enforce the bounds; note that the
// nanosecond clock is populated as soon as Alpenglow migration is complete.
let Some(parent_time_nanos) = parent_bank.get_nanosecond_clock() else {
return Ok(());
};

let parent_slot = parent_bank.slot();
let current_time_nanos = footer.block_producer_time_nanos as i64;
let current_slot = bank.slot();

let (lower_bound_nanos, upper_bound_nanos) =
Self::nanosecond_time_bounds(parent_slot, parent_time_nanos, current_slot);

let is_valid =
lower_bound_nanos <= current_time_nanos && current_time_nanos <= upper_bound_nanos;

match is_valid {
true => Ok(()),
false => Err(BlockComponentProcessorError::NanosecondClockOutOfBounds),
}
}

/// Given the parent slot, parent time, and slot, calculate the lower and upper
/// bounds for the block producer time. We return (lower_bound, upper_bound), where both bounds
/// are inclusive. I.e., the working bank time is valid if
Expand All @@ -189,9 +163,9 @@ impl BlockComponentProcessor {
(min_working_bank_time, max_working_bank_time)
}

pub fn update_bank_with_footer(bank: Arc<Bank>, footer: &BlockFooterV1) {
pub fn update_bank_with_footer(bank: Arc<Bank>, footer: &ValidatedBlockFooter) {
// Update clock sysvar
bank.update_clock_from_footer(footer.block_producer_time_nanos as i64);
bank.update_clock_from_footer(footer.block_producer_time_ns());

// TODO: rewards
}
Expand Down
1 change: 1 addition & 0 deletions runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ pub mod stakes;
pub mod static_ids;
pub mod status_cache;
pub mod transaction_batch;
pub mod validated_block_footer;
pub mod vote_sender_types;

#[macro_use]
Expand Down
126 changes: 126 additions & 0 deletions runtime/src/validated_block_footer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
use {
crate::{bank::Bank, block_component_processor::BlockComponentProcessor},
solana_entry::block_component::BlockFooterV1,
solana_hash::Hash,
thiserror::Error,
};

#[derive(Debug, Error, PartialEq, Eq)]
pub enum ValidatedBlockFooterError {
#[error("clock out of bounds")]
ClockOutOfBounds,
#[error("clock overflow")]
ClockOverflow,
#[error("bank hash mismatch")]
BankHashMismatch,
}

pub struct ValidatedBlockFooter {
bank_hash: Hash,
block_producer_time_ns: i64,
block_user_agent: Vec<u8>,
}

impl ValidatedBlockFooter {
/// Creates a new [`ValidatedBlockFooter`] for the block the block producer.
///
/// Warning! No validation checks are performed, this should not be called during replay but only during block production.
pub fn new_unchecked_for_block_producer(footer: BlockFooterV1) -> Self {
let BlockFooterV1 {
bank_hash,
block_producer_time_nanos,
block_user_agent,
} = footer;
Self {
bank_hash,
block_producer_time_ns: block_producer_time_nanos.try_into().unwrap(),
block_user_agent,
}
}

/// Creates a [`ValidatedBlockFooter`] and fails if the footer is found to be invalid.
pub fn try_new(
parent_bank: &Bank,
bank: &Bank,
footer: BlockFooterV1,
) -> Result<Self, ValidatedBlockFooterError> {
let BlockFooterV1 {
bank_hash,
block_producer_time_nanos,
block_user_agent,
} = footer;
let block_producer_time_ns = validate_clock(parent_bank, bank, block_producer_time_nanos)?;
if bank_hash != bank.hash() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

feedback from Ashwin. This check cannot be performed yet because the bank is not frozen yet.

return Err(ValidatedBlockFooterError::BankHashMismatch);
}
Ok(Self {
bank_hash,
block_producer_time_ns,
block_user_agent,
})
}

pub fn block_producer_time_ns(&self) -> i64 {
self.block_producer_time_ns
}

pub fn bank_hash(&self) -> Hash {
self.bank_hash
}

pub fn block_user_agent(&self) -> &[u8] {
&self.block_user_agent
}
}

impl From<ValidatedBlockFooter> for BlockFooterV1 {
fn from(footer: ValidatedBlockFooter) -> Self {
let ValidatedBlockFooter {
bank_hash,
block_producer_time_ns,
block_user_agent,
} = footer;
let block_producer_time_nanos = block_producer_time_ns
.try_into()
.expect("i64 to u64 should not overflow");
BlockFooterV1 {
bank_hash,
block_producer_time_nanos,
block_user_agent,
}
}
}

fn validate_clock(
parent_bank: &Bank,
bank: &Bank,
block_producer_time_ns: u64,
) -> Result<i64, ValidatedBlockFooterError> {
let block_producer_time_ns = block_producer_time_ns
.try_into()
.map_err(|_| ValidatedBlockFooterError::ClockOverflow)?;

// Get parent time from nanosecond clock account
// If nanosecond clock hasn't been populated, don't enforce the bounds; note that the
// nanosecond clock is populated as soon as Alpenglow migration is complete.
let Some(parent_time_nanos) = parent_bank.get_nanosecond_clock() else {
return Ok(block_producer_time_ns);
};

let parent_slot = parent_bank.slot();
let current_slot = bank.slot();

let (lower_bound_ns, upper_bound_ns) = BlockComponentProcessor::nanosecond_time_bounds(
parent_slot,
parent_time_nanos,
current_slot,
);

let is_valid =
lower_bound_ns <= block_producer_time_ns && block_producer_time_ns <= upper_bound_ns;

match is_valid {
true => Ok(block_producer_time_ns),
false => Err(ValidatedBlockFooterError::ClockOutOfBounds),
}
}