Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Warp sync
  • Loading branch information
davxy committed Dec 13, 2022
commit cc4a33e9634ce0432dd7a837d11dd86e925d678d
10 changes: 10 additions & 0 deletions bin/node-sassafras/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,8 @@ impl_runtime_apis! {
}

impl sp_consensus_sassafras::SassafrasApi<Block> for Runtime {
// TODO-SASS-P3: this maybe is not necessary...
// we can fetch the information using current_epoch()
fn configuration() -> sp_consensus_sassafras::SassafrasConfiguration {
sp_consensus_sassafras::SassafrasConfiguration {
slot_duration: SLOT_DURATION_IN_MILLISECONDS,
Expand All @@ -399,6 +401,14 @@ impl_runtime_apis! {
Sassafras::slot_ticket(slot)
}

fn current_epoch() -> sp_consensus_sassafras::Epoch {
Sassafras::current_epoch()
}

fn next_epoch() -> sp_consensus_sassafras::Epoch {
Sassafras::next_epoch()
}

fn generate_key_ownership_proof(
_slot: sp_consensus_sassafras::Slot,
_authority_id: sp_consensus_sassafras::AuthorityId,
Expand Down
88 changes: 88 additions & 0 deletions client/consensus/sassafras/src/block_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

use super::*;
use sc_client_api::{AuxDataOperations, FinalityNotification, PreCommitActions};
use sp_blockchain::BlockStatus;

/// Block-import handler for Sassafras.
///
Expand Down Expand Up @@ -230,6 +231,77 @@ where
}
}

impl<Block, Client, Inner> SassafrasBlockImport<Block, Client, Inner>
where
Block: BlockT,
Inner: BlockImport<Block, Transaction = sp_api::TransactionFor<Client, Block>> + Send + Sync,
Inner::Error: Into<ConsensusError>,
Client: HeaderBackend<Block>
+ HeaderMetadata<Block, Error = sp_blockchain::Error>
+ AuxStore
+ ProvideRuntimeApi<Block>
+ Send
+ Sync,
Client::Api: SassafrasApi<Block> + ApiExt<Block>,
{
/// Import whole state after a warp sync.
///
/// This function makes multiple transactions to the DB. If one of them fails we may
/// end up in an inconsistent state and have to resync
async fn import_state(
&mut self,
mut block: BlockImportParams<Block, sp_api::TransactionFor<Client, Block>>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, ConsensusError> {
let hash = block.post_hash();
let parent_hash = *block.header.parent_hash();
let number = *block.header.number();

// Check for the unit tag.
block.remove_intermediate::<()>(INTERMEDIATE_KEY)?;

// Import as best
block.fork_choice = Some(ForkChoiceStrategy::Custom(true));

// Reset block weight
aux_schema::write_block_weight(hash, 0, |values| {
block
.auxiliary
.extend(values.iter().map(|(k, v)| (k.to_vec(), Some(v.to_vec()))))
});

// First make the client import the state
let aux = match self.inner.import_block(block, new_cache).await {
Ok(ImportResult::Imported(aux)) => aux,
Ok(r) =>
return Err(ConsensusError::ClientImport(format!(
"Unexpected import result: {:?}",
r
))),
Err(e) => return Err(e.into()),
};

// Read epoch info from the imported state
let block_id = BlockId::Hash(hash);
let curr_epoch = self.client.runtime_api().current_epoch(&block_id).map_err(|e| {
ConsensusError::ClientImport(sassafras_err::<Block>(Error::RuntimeApi(e)).into())
})?;
let next_epoch = self.client.runtime_api().next_epoch(&block_id).map_err(|e| {
ConsensusError::ClientImport(sassafras_err::<Block>(Error::RuntimeApi(e)).into())
})?;

let mut epoch_changes = self.epoch_changes.shared_data();
epoch_changes.reset(parent_hash, hash, number, curr_epoch.into(), next_epoch.into());

aux_schema::write_epoch_changes::<Block, _, _>(&*epoch_changes, |insert| {
self.client.insert_aux(insert, [])
})
.map_err(|e| ConsensusError::ClientImport(e.to_string()))?;

Ok(ImportResult::Imported(aux))
}
}

#[async_trait::async_trait]
impl<Block, Client, Inner> BlockImport<Block> for SassafrasBlockImport<Block, Client, Inner>
where
Expand All @@ -255,6 +327,22 @@ where
let hash = block.post_hash();
let number = *block.header.number();

// Early exit if block already in chain, otherwise the check for epoch changes
// will error when trying to re-import
match self.client.status(BlockId::Hash(hash)) {
Ok(BlockStatus::InChain) => {
block.remove_intermediate::<SassafrasIntermediate<Block>>(INTERMEDIATE_KEY)?;
block.fork_choice = Some(ForkChoiceStrategy::Custom(false));
return self.inner.import_block(block, new_cache).await.map_err(Into::into)
},
Ok(BlockStatus::Unknown) => {},
Err(e) => return Err(ConsensusError::ClientImport(e.to_string())),
}

if block.with_state() {
return self.import_state(block, new_cache).await
}

let viable_epoch_desc = block
.remove_intermediate::<SassafrasIntermediate<Block>>(INTERMEDIATE_KEY)?
.epoch_descriptor;
Expand Down
15 changes: 13 additions & 2 deletions client/consensus/sassafras/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,30 @@ fn sassafras_err<B: BlockT>(error: Error<B>) -> Error<B> {
error
}

/// Sassafras epoch information
/// Sassafras epoch information augmented with private tickets information.
#[derive(Encode, Decode, PartialEq, Eq, Clone, Debug)]
pub struct Epoch {
/// The epoch index.
pub epoch_idx: u64,
/// The starting slot of the epoch.
pub start_slot: Slot,
/// Epoch configuration
/// Epoch configuration.
pub config: SassafrasConfiguration,
/// Tickets auxiliary data.
pub tickets_aux: BTreeMap<VRFOutput, (AuthorityIndex, TicketAux)>,
}

impl From<sp_consensus_sassafras::Epoch> for Epoch {
fn from(epoch: sp_consensus_sassafras::Epoch) -> Self {
Epoch {
epoch_idx: epoch.epoch_idx,
start_slot: epoch.start_slot,
config: epoch.config,
tickets_aux: BTreeMap::new(),
}
}
}

impl EpochT for Epoch {
type NextEpochDescriptor = NextEpochDescriptor;
type Slot = Slot;
Expand Down
95 changes: 53 additions & 42 deletions client/consensus/sassafras/src/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ struct VerificationParams<'a, B: 'a + BlockT> {
slot_now: Slot,
/// Epoch descriptor of the epoch this block _should_ be under, if it's valid.
epoch: &'a Epoch,
/// Origin
origin: BlockOrigin,
/// Expected ticket for this block.
ticket: Option<Ticket>,
}
Expand All @@ -57,7 +59,7 @@ struct VerifiedHeaderInfo {
fn check_header<B: BlockT + Sized>(
params: VerificationParams<B>,
) -> Result<CheckedHeader<B::Header, VerifiedHeaderInfo>, Error<B>> {
let VerificationParams { mut header, pre_digest, slot_now, epoch, ticket } = params;
let VerificationParams { mut header, pre_digest, slot_now, epoch, origin, ticket } = params;
let config = &epoch.config;

// Check that the slot is not in the future, with some drift being allowed.
Expand Down Expand Up @@ -109,10 +111,11 @@ fn check_header<B: BlockT + Sized>(
log::warn!(target: "sassafras", "🌳 Unexpected secondary authoring mechanism");
return Err(Error::UnexpectedAuthoringMechanism)
},
(None, Some(_)) => {
log::warn!(target: "sassafras", "🌳 Unexpected primary authoring mechanism");
return Err(Error::UnexpectedAuthoringMechanism)
},
(None, Some(_)) =>
if origin != BlockOrigin::NetworkInitialSync {
log::warn!(target: "sassafras", "🌳 Unexpected primary authoring mechanism");
return Err(Error::UnexpectedAuthoringMechanism)
},
}

// Check slot-vrf proof
Expand Down Expand Up @@ -299,27 +302,30 @@ where
{
async fn verify(
&mut self,
mut import_params: BlockImportParams<Block, ()>,
mut block: BlockImportParams<Block, ()>,
) -> BlockVerificationResult<Block> {
trace!(
target: "sassafras",
"🌳 Verifying origin: {:?} header: {:?} justification(s): {:?} body: {:?}",
import_params.origin,
import_params.header,
import_params.justifications,
import_params.body,
block.origin,
block.header,
block.justifications,
block.body,
);

if import_params.with_state() {
if block.with_state() {
// When importing whole state we don't calculate epoch descriptor, but rather
// read it from the state after import. We also skip all verifications
// because there's no parent state and we trust the sync module to verify
// that the state is correct and finalized.
return Ok((import_params, Default::default()))
// Just insert a tag to notify that this is indeed a Sassafras block to the
// `BlockImport` implementation.
block.insert_intermediate(INTERMEDIATE_KEY, ());
return Ok((block, Default::default()))
}

let hash = import_params.header.hash();
let parent_hash = *import_params.header.parent_hash();
let hash = block.header.hash();
let parent_hash = *block.header.parent_hash();

let create_inherent_data_providers = self
.create_inherent_data_providers
Expand All @@ -334,7 +340,7 @@ where
.header_metadata(parent_hash)
.map_err(Error::<Block>::FetchParentHeader)?;

let pre_digest = find_pre_digest::<Block>(&import_params.header)?;
let pre_digest = find_pre_digest::<Block>(&block.header)?;

let (checked_header, epoch_descriptor) = {
let epoch_changes = self.epoch_changes.shared_data();
Expand All @@ -355,13 +361,15 @@ where
.client
.runtime_api()
.slot_ticket(&BlockId::Hash(parent_hash), pre_digest.slot)
.map_err(|err| err.to_string())?;
.ok()
.unwrap_or_else(|| None);

let verification_params = VerificationParams {
header: import_params.header.clone(),
header: block.header.clone(),
pre_digest: &pre_digest,
slot_now,
epoch: viable_epoch.as_ref(),
origin: block.origin,
ticket,
};
let checked_header = check_header::<Block>(verification_params)?;
Expand All @@ -378,9 +386,9 @@ where
.check_and_report_equivocation(
slot_now,
pre_digest.slot,
&import_params.header,
&block.header,
&verified_info.authority_id,
&import_params.origin,
&block.origin,
)
.await
{
Expand All @@ -390,24 +398,27 @@ where
// If the body is passed through, we need to use the runtime to check that the
// internally-set timestamp in the inherents actually matches the slot set in the
// seal.
if let Some(inner_body) = import_params.body {
let mut inherent_data = create_inherent_data_providers
.create_inherent_data()
.map_err(Error::<Block>::CreateInherents)?;
inherent_data.sassafras_replace_inherent_data(pre_digest.slot);
let block = Block::new(pre_header.clone(), inner_body);

self.check_inherents(
block.clone(),
BlockId::Hash(parent_hash),
inherent_data,
create_inherent_data_providers,
import_params.origin.into(),
)
.await?;

let (_, inner_body) = block.deconstruct();
import_params.body = Some(inner_body);
if let Some(inner_body) = block.body {
let new_block = Block::new(pre_header.clone(), inner_body);

if !block.state_action.skip_execution_checks() {
// TODO-SASS-P3 :??? DOC
let mut inherent_data = create_inherent_data_providers
.create_inherent_data()
.map_err(Error::<Block>::CreateInherents)?;
inherent_data.sassafras_replace_inherent_data(pre_digest.slot);
self.check_inherents(
new_block.clone(),
BlockId::Hash(parent_hash),
inherent_data,
create_inherent_data_providers,
block.origin.into(),
)
.await?;
}

let (_, inner_body) = new_block.deconstruct();
block.body = Some(inner_body);
}

trace!(target: "sassafras", "🌳 Checked {:?}; importing.", pre_header);
Expand All @@ -418,15 +429,15 @@ where
"pre_header" => ?pre_header,
);

import_params.header = pre_header;
import_params.post_hash = Some(hash);
import_params.post_digests.push(verified_info.seal);
import_params.insert_intermediate(
block.header = pre_header;
block.post_hash = Some(hash);
block.post_digests.push(verified_info.seal);
block.insert_intermediate(
INTERMEDIATE_KEY,
SassafrasIntermediate::<Block> { epoch_descriptor },
);

Ok((import_params, Default::default()))
Ok((block, Default::default()))
},
CheckedHeader::Deferred(a, b) => {
debug!(target: "sassafras", "🌳 Checking {:?} failed; {:?}, {:?}.", hash, a, b);
Expand Down
Loading