From 39d7a51777a5f01a178c95b4cc4b51dbc5436dcc Mon Sep 17 00:00:00 2001 From: alindima Date: Wed, 2 Oct 2024 16:19:08 +0300 Subject: [PATCH] use v2 receipts in collation generation --- polkadot/node/collation-generation/Cargo.toml | 1 + .../node/collation-generation/src/error.rs | 4 + polkadot/node/collation-generation/src/lib.rs | 690 ++++++++++-------- 3 files changed, 380 insertions(+), 315 deletions(-) diff --git a/polkadot/node/collation-generation/Cargo.toml b/polkadot/node/collation-generation/Cargo.toml index 855b6b0e86eb..777458673f5b 100644 --- a/polkadot/node/collation-generation/Cargo.toml +++ b/polkadot/node/collation-generation/Cargo.toml @@ -21,6 +21,7 @@ sp-core = { workspace = true, default-features = true } sp-maybe-compressed-blob = { workspace = true, default-features = true } thiserror = { workspace = true } codec = { features = ["bit-vec", "derive"], workspace = true } +schnellru = { workspace = true } [dev-dependencies] polkadot-node-subsystem-test-helpers = { workspace = true } diff --git a/polkadot/node/collation-generation/src/error.rs b/polkadot/node/collation-generation/src/error.rs index f04e3c4f20b4..36f25d6b772a 100644 --- a/polkadot/node/collation-generation/src/error.rs +++ b/polkadot/node/collation-generation/src/error.rs @@ -32,6 +32,10 @@ pub enum Error { Erasure(#[from] polkadot_erasure_coding::Error), #[error("Parachain backing state not available in runtime.")] MissingParaBackingState, + #[error("Collation submitted before initialization")] + SubmittedBeforeInit, + #[error("Claim queue runtime API not available")] + ClaimQueueNotAvailable, } pub type Result = std::result::Result; diff --git a/polkadot/node/collation-generation/src/lib.rs b/polkadot/node/collation-generation/src/lib.rs index f04f69cbd380..875e5a74e0f3 100644 --- a/polkadot/node/collation-generation/src/lib.rs +++ b/polkadot/node/collation-generation/src/lib.rs @@ -32,27 +32,35 @@ #![deny(missing_docs)] use codec::Encode; +use error::{Error, Result}; use futures::{channel::oneshot, future::FutureExt, join, select}; use polkadot_node_primitives::{ AvailableData, Collation, CollationGenerationConfig, CollationSecondedSignal, PoV, SubmitCollationParams, }; use polkadot_node_subsystem::{ - messages::{CollationGenerationMessage, CollatorProtocolMessage}, + messages::{CollationGenerationMessage, CollatorProtocolMessage, RuntimeApiMessage}, overseer, ActiveLeavesUpdate, FromOrchestra, OverseerSignal, RuntimeApiError, SpawnedSubsystem, - SubsystemContext, SubsystemError, SubsystemResult, + SubsystemContext, SubsystemError, SubsystemResult, SubsystemSender, }; use polkadot_node_subsystem_util::{ request_async_backing_params, request_availability_cores, request_para_backing_state, - request_persisted_validation_data, request_validation_code, request_validation_code_hash, - request_validators, runtime::fetch_claim_queue, + request_persisted_validation_data, request_session_index_for_child, request_validation_code, + request_validation_code_hash, request_validators, + runtime::{fetch_claim_queue, request_node_features}, }; use polkadot_primitives::{ collator_signature_payload, - vstaging::{CandidateReceiptV2 as CandidateReceipt, CoreState}, - CandidateCommitments, CandidateDescriptor, CollatorPair, CoreIndex, Hash, Id as ParaId, - OccupiedCoreAssumption, PersistedValidationData, ScheduledCore, ValidationCodeHash, + node_features::FeatureIndex, + vstaging::{ + transpose_claim_queue, CandidateDescriptorV2, CandidateReceiptV2 as CandidateReceipt, + CommittedCandidateReceiptV2, CoreState, TransposedClaimQueue, + }, + AsyncBackingParams, CandidateCommitments, CandidateDescriptor, CollatorPair, CoreIndex, Hash, + Id as ParaId, NodeFeatures, OccupiedCoreAssumption, PersistedValidationData, ScheduledCore, + SessionIndex, ValidationCodeHash, }; +use schnellru::{ByLength, LruMap}; use sp_core::crypto::Pair; use std::sync::Arc; @@ -69,6 +77,7 @@ const LOG_TARGET: &'static str = "parachain::collation-generation"; /// Collation Generation Subsystem pub struct CollationGenerationSubsystem { config: Option>, + session_info: SessionInfoCache, metrics: Metrics, } @@ -76,7 +85,7 @@ pub struct CollationGenerationSubsystem { impl CollationGenerationSubsystem { /// Create a new instance of the `CollationGenerationSubsystem`. pub fn new(metrics: Metrics) -> Self { - Self { config: None, metrics } + Self { config: None, metrics, session_info: SessionInfoCache::new() } } /// Run this subsystem @@ -117,19 +126,10 @@ impl CollationGenerationSubsystem { activated, .. }))) => { - // follow the procedure from the guide - if let Some(config) = &self.config { - let metrics = self.metrics.clone(); - if let Err(err) = handle_new_activations( - config.clone(), - activated.into_iter().map(|v| v.hash), - ctx, - metrics, - ) - .await - { - gum::warn!(target: LOG_TARGET, err = ?err, "failed to handle new activations"); - } + if let Err(err) = + self.handle_new_activations(activated.into_iter().map(|v| v.hash), ctx).await + { + gum::warn!(target: LOG_TARGET, err = ?err, "failed to handle new activations"); } false @@ -154,14 +154,8 @@ impl CollationGenerationSubsystem { Ok(FromOrchestra::Communication { msg: CollationGenerationMessage::SubmitCollation(params), }) => { - if let Some(config) = &self.config { - if let Err(err) = - handle_submit_collation(params, config, ctx, &self.metrics).await - { - gum::error!(target: LOG_TARGET, ?err, "Failed to submit collation"); - } - } else { - gum::error!(target: LOG_TARGET, "Collation submitted before initialization"); + if let Err(err) = self.handle_submit_collation(params, ctx).await { + gum::error!(target: LOG_TARGET, ?err, "Failed to submit collation"); } false @@ -178,95 +172,143 @@ impl CollationGenerationSubsystem { }, } } -} -#[overseer::subsystem(CollationGeneration, error=SubsystemError, prefix=self::overseer)] -impl CollationGenerationSubsystem { - fn start(self, ctx: Context) -> SpawnedSubsystem { - let future = async move { - self.run(ctx).await; - Ok(()) - } - .boxed(); + async fn handle_submit_collation( + &mut self, + params: SubmitCollationParams, + ctx: &mut Context, + ) -> Result<()> { + let Some(config) = &self.config else { + return Err(Error::SubmittedBeforeInit); + }; + let _timer = self.metrics.time_submit_collation(); - SpawnedSubsystem { name: "collation-generation-subsystem", future } - } -} + let SubmitCollationParams { + relay_parent, + collation, + parent_head, + validation_code_hash, + result_sender, + core_index, + } = params; -#[overseer::contextbounds(CollationGeneration, prefix = self::overseer)] -async fn handle_new_activations( - config: Arc, - activated: impl IntoIterator, - ctx: &mut Context, - metrics: Metrics, -) -> crate::error::Result<()> { - // follow the procedure from the guide: - // https://paritytech.github.io/polkadot-sdk/book/node/collators/collation-generation.html - - // If there is no collation function provided, bail out early. - // Important: Lookahead collator and slot based collator do not use `CollatorFn`. - if config.collator.is_none() { - return Ok(()) + // We need to swap the parent-head data, but all other fields here will be correct. + let mut validation_data = match request_persisted_validation_data( + relay_parent, + config.para_id, + OccupiedCoreAssumption::TimedOut, + ctx.sender(), + ) + .await + .await?? + { + Some(v) => v, + None => { + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %config.para_id, + "No validation data for para - does it exist at this relay-parent?", + ); + return Ok(()) + }, + }; + + validation_data.parent_head = parent_head; + + let session_index = + request_session_index_for_child(relay_parent, ctx.sender()).await.await??; + + let session_info = self.session_info.get(relay_parent, session_index, ctx.sender()).await?; + let collation = PreparedCollation { + collation, + relay_parent, + para_id: config.para_id, + validation_data, + validation_code_hash, + n_validators: session_info.n_validators, + core_index, + session_index, + }; + + let claim_queue = fetch_claim_queue(ctx.sender(), relay_parent) + .await + .map_err(Error::UtilRuntime)? + .ok_or(Error::ClaimQueueNotAvailable)?; + + construct_and_distribute_receipt( + collation, + config.key.clone(), + ctx.sender(), + result_sender, + &mut self.metrics, + session_info.v2_receipts, + &transpose_claim_queue(claim_queue.0), + ) + .await; + + Ok(()) } - let para_id = config.para_id; + async fn handle_new_activations( + &mut self, + activated: impl IntoIterator, + ctx: &mut Context, + ) -> Result<()> { + let Some(config) = &self.config else { + return Ok(()); + }; - let _overall_timer = metrics.time_new_activations(); + // If there is no collation function provided, bail out early. + // Important: Lookahead collator and slot based collator do not use `CollatorFn`. + if config.collator.is_none() { + return Ok(()) + } - for relay_parent in activated { - let _relay_parent_timer = metrics.time_new_activations_relay_parent(); + let para_id = config.para_id; - let (availability_cores, validators, async_backing_params) = join!( - request_availability_cores(relay_parent, ctx.sender()).await, - request_validators(relay_parent, ctx.sender()).await, - request_async_backing_params(relay_parent, ctx.sender()).await, - ); + let _overall_timer = self.metrics.time_new_activations(); - let availability_cores = availability_cores??; - let async_backing_params = async_backing_params?.ok(); - let n_validators = validators??.len(); - let maybe_claim_queue = fetch_claim_queue(ctx.sender(), relay_parent) - .await - .map_err(crate::error::Error::UtilRuntime)?; - - // The loop bellow will fill in cores that the para is allowed to build on. - let mut cores_to_build_on = Vec::new(); - - // This assumption refers to all cores of the parachain, taking elastic scaling - // into account. - let mut para_assumption = None; - for (core_idx, core) in availability_cores.into_iter().enumerate() { - // This nested assumption refers only to the core being iterated. - let (core_assumption, scheduled_core) = match core { - CoreState::Scheduled(scheduled_core) => - (OccupiedCoreAssumption::Free, scheduled_core), - CoreState::Occupied(occupied_core) => match async_backing_params { - Some(params) if params.max_candidate_depth >= 1 => { + for relay_parent in activated { + let _relay_parent_timer = self.metrics.time_new_activations_relay_parent(); + + let session_index = + request_session_index_for_child(relay_parent, ctx.sender()).await.await??; + + let session_info = + self.session_info.get(relay_parent, session_index, ctx.sender()).await?; + let async_backing_params = session_info.async_backing_params; + let n_validators = session_info.n_validators; + + let availability_cores = + request_availability_cores(relay_parent, ctx.sender()).await.await??; + + let claim_queue = fetch_claim_queue(ctx.sender(), relay_parent) + .await + .map_err(Error::UtilRuntime)? + .ok_or(Error::ClaimQueueNotAvailable)?; + + // The loop bellow will fill in cores that the para is allowed to build on. + let mut cores_to_build_on = Vec::new(); + + for (core_idx, core) in availability_cores.into_iter().enumerate() { + let scheduled_core = match core { + CoreState::Scheduled(scheduled_core) => scheduled_core, + CoreState::Occupied(_) if async_backing_params.max_candidate_depth >= 1 => { // maximum candidate depth when building on top of a block // pending availability is necessarily 1 - the depth of the // pending block is 0 so the child has depth 1. - // Use claim queue if available, or fallback to `next_up_on_available` - let res = match maybe_claim_queue { - Some(ref claim_queue) => { - // read what's in the claim queue for this core at depth 0. - claim_queue - .get_claim_for(CoreIndex(core_idx as u32), 0) - .map(|para_id| ScheduledCore { para_id, collator: None }) - }, - None => { - // Runtime doesn't support claim queue runtime api. Fallback to - // `next_up_on_available` - occupied_core.next_up_on_available - }, - }; - - match res { - Some(res) => (OccupiedCoreAssumption::Included, res), + // read what's in the claim queue for this core at depth 0. + match claim_queue + .get_claim_for(CoreIndex(core_idx as u32), 0) + .map(|para_id| ScheduledCore { para_id, collator: None }) + { + Some(scheduled_core) => scheduled_core, None => continue, } }, - _ => { + CoreState::Occupied(_) => { gum::trace!( target: LOG_TARGET, core_idx = %core_idx, @@ -275,235 +317,222 @@ async fn handle_new_activations( ); continue }, - }, - CoreState::Free => { + CoreState::Free => { + gum::trace!( + target: LOG_TARGET, + core_idx = %core_idx, + "core is not assigned to any para. Keep going.", + ); + continue + }, + }; + + if scheduled_core.para_id != config.para_id { gum::trace!( target: LOG_TARGET, core_idx = %core_idx, - "core is not assigned to any para. Keep going.", + relay_parent = ?relay_parent, + our_para = %config.para_id, + their_para = %scheduled_core.para_id, + "core is not assigned to our para. Keep going.", ); - continue - }, - }; - - if scheduled_core.para_id != config.para_id { - gum::trace!( - target: LOG_TARGET, - core_idx = %core_idx, - relay_parent = ?relay_parent, - our_para = %config.para_id, - their_para = %scheduled_core.para_id, - "core is not assigned to our para. Keep going.", - ); - } else { - // This does not work for elastic scaling, but it should be enough for single - // core parachains. If async backing runtime is available we later override - // the assumption based on the `para_backing_state` API response. - para_assumption = Some(core_assumption); - // Accumulate cores for building collation(s) outside the loop. - cores_to_build_on.push(CoreIndex(core_idx as u32)); + } else { + // Accumulate cores for building collation(s) outside the loop. + cores_to_build_on.push(CoreIndex(core_idx as u32)); + } } - } - - // Skip to next relay parent if there is no core assigned to us. - if cores_to_build_on.is_empty() { - continue - } - // If at least one core is assigned to us, `para_assumption` is `Some`. - let Some(mut para_assumption) = para_assumption else { continue }; + // Skip to next relay parent if there is no core assigned to us. + if cores_to_build_on.is_empty() { + continue + } - // If it is none it means that neither async backing or elastic scaling (which - // depends on it) are supported. We'll use the `para_assumption` we got from - // iterating cores. - if async_backing_params.is_some() { - // We are being very optimistic here, but one of the cores could pend availability some - // more block, ore even time out. + // We are being very optimistic here, but one of the cores could be pending availability + // for some more blocks, or even time out. // For timeout assumption the collator can't really know because it doesn't receive // bitfield gossip. let para_backing_state = request_para_backing_state(relay_parent, config.para_id, ctx.sender()) .await .await?? - .ok_or(crate::error::Error::MissingParaBackingState)?; + .ok_or(Error::MissingParaBackingState)?; - // Override the assumption about the para's assigned cores. - para_assumption = if para_backing_state.pending_availability.is_empty() { + let para_assumption = if para_backing_state.pending_availability.is_empty() { OccupiedCoreAssumption::Free } else { OccupiedCoreAssumption::Included - } - } + }; - gum::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - our_para = %para_id, - ?para_assumption, - "Occupied core(s) assumption", - ); + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %para_id, + ?para_assumption, + "Occupied core(s) assumption", + ); - let mut validation_data = match request_persisted_validation_data( - relay_parent, - para_id, - para_assumption, - ctx.sender(), - ) - .await - .await?? - { - Some(v) => v, - None => { - gum::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - our_para = %para_id, - "validation data is not available", - ); - continue - }, - }; + let mut validation_data = match request_persisted_validation_data( + relay_parent, + para_id, + para_assumption, + ctx.sender(), + ) + .await + .await?? + { + Some(v) => v, + None => { + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %para_id, + "validation data is not available", + ); + continue + }, + }; - let validation_code_hash = match obtain_validation_code_hash_with_assumption( - relay_parent, - para_id, - para_assumption, - ctx.sender(), - ) - .await? - { - Some(v) => v, - None => { - gum::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - our_para = %para_id, - "validation code hash is not found.", - ); - continue - }, - }; + let validation_code_hash = match obtain_validation_code_hash_with_assumption( + relay_parent, + para_id, + para_assumption, + ctx.sender(), + ) + .await? + { + Some(v) => v, + None => { + gum::debug!( + target: LOG_TARGET, + relay_parent = ?relay_parent, + our_para = %para_id, + "validation code hash is not found.", + ); + continue + }, + }; - let task_config = config.clone(); - let metrics = metrics.clone(); - let mut task_sender = ctx.sender().clone(); - - ctx.spawn( - "chained-collation-builder", - Box::pin(async move { - for core_index in cores_to_build_on { - let collator_fn = match task_config.collator.as_ref() { - Some(x) => x, - None => return, - }; - - let (collation, result_sender) = - match collator_fn(relay_parent, &validation_data).await { - Some(collation) => collation.into_inner(), - None => { - gum::debug!( - target: LOG_TARGET, - ?para_id, - "collator returned no collation on collate", - ); - return - }, + let task_config = config.clone(); + let metrics = self.metrics.clone(); + let mut task_sender = ctx.sender().clone(); + + let transposed_claim_queue = transpose_claim_queue(claim_queue.0); + + ctx.spawn( + "chained-collation-builder", + Box::pin(async move { + for core_index in cores_to_build_on { + let collator_fn = match task_config.collator.as_ref() { + Some(x) => x, + None => return, }; - let parent_head = collation.head_data.clone(); - construct_and_distribute_receipt( - PreparedCollation { - collation, - para_id, - relay_parent, - validation_data: validation_data.clone(), - validation_code_hash, - n_validators, - core_index, - }, - task_config.key.clone(), - &mut task_sender, - result_sender, - &metrics, - ) - .await; - - // Chain the collations. All else stays the same as we build the chained - // collation on same relay parent. - validation_data.parent_head = parent_head; - } - }), - )?; + let (collation, result_sender) = + match collator_fn(relay_parent, &validation_data).await { + Some(collation) => collation.into_inner(), + None => { + gum::debug!( + target: LOG_TARGET, + ?para_id, + "collator returned no collation on collate", + ); + return + }, + }; + + let parent_head = collation.head_data.clone(); + construct_and_distribute_receipt( + PreparedCollation { + collation, + para_id, + relay_parent, + validation_data: validation_data.clone(), + validation_code_hash, + n_validators, + core_index, + session_index, + }, + task_config.key.clone(), + &mut task_sender, + result_sender, + &metrics, + session_info.v2_receipts, + &transposed_claim_queue, + ) + .await; + + // Chain the collations. All else stays the same as we build the chained + // collation on same relay parent. + validation_data.parent_head = parent_head; + } + }), + )?; + } + + Ok(()) } +} - Ok(()) +#[overseer::subsystem(CollationGeneration, error=SubsystemError, prefix=self::overseer)] +impl CollationGenerationSubsystem { + fn start(self, ctx: Context) -> SpawnedSubsystem { + let future = async move { + self.run(ctx).await; + Ok(()) + } + .boxed(); + + SpawnedSubsystem { name: "collation-generation-subsystem", future } + } } -#[overseer::contextbounds(CollationGeneration, prefix = self::overseer)] -async fn handle_submit_collation( - params: SubmitCollationParams, - config: &CollationGenerationConfig, - ctx: &mut Context, - metrics: &Metrics, -) -> crate::error::Result<()> { - let _timer = metrics.time_submit_collation(); +#[derive(Clone)] +struct SessionInfo { + v2_receipts: bool, + n_validators: usize, + async_backing_params: AsyncBackingParams, +} - let SubmitCollationParams { - relay_parent, - collation, - parent_head, - validation_code_hash, - result_sender, - core_index, - } = params; +struct SessionInfoCache(LruMap); - let validators = request_validators(relay_parent, ctx.sender()).await.await??; - let n_validators = validators.len(); +impl SessionInfoCache { + fn new() -> Self { + Self(LruMap::new(ByLength::new(2))) + } - // We need to swap the parent-head data, but all other fields here will be correct. - let mut validation_data = match request_persisted_validation_data( - relay_parent, - config.para_id, - OccupiedCoreAssumption::TimedOut, - ctx.sender(), - ) - .await - .await?? - { - Some(v) => v, - None => { - gum::debug!( - target: LOG_TARGET, - relay_parent = ?relay_parent, - our_para = %config.para_id, - "No validation data for para - does it exist at this relay-parent?", - ); - return Ok(()) - }, - }; + async fn get>( + &mut self, + relay_parent: Hash, + session_index: SessionIndex, + sender: &mut Sender, + ) -> Result { + if let Some(info) = self.0.get(&session_index) { + return Ok(info.clone()) + } - validation_data.parent_head = parent_head; + let (validators, async_backing_params) = join!( + request_validators(relay_parent, &mut sender.clone()).await, + request_async_backing_params(relay_parent, &mut sender.clone()).await, + ); - let collation = PreparedCollation { - collation, - relay_parent, - para_id: config.para_id, - validation_data, - validation_code_hash, - n_validators, - core_index, - }; + let node_features = request_node_features(relay_parent, session_index, sender) + .await? + .unwrap_or(NodeFeatures::EMPTY); - construct_and_distribute_receipt( - collation, - config.key.clone(), - ctx.sender(), - result_sender, - metrics, - ) - .await; - - Ok(()) + let n_validators = validators??.len(); + let async_backing_params = async_backing_params??; + + let info = SessionInfo { + v2_receipts: node_features + .get(FeatureIndex::CandidateReceiptV2 as usize) + .map(|b| *b) + .unwrap_or(false), + n_validators, + async_backing_params, + }; + self.0.insert(session_index, info); + Ok(self.0.get(&session_index).expect("Just inserted").clone()) + } } struct PreparedCollation { @@ -514,6 +543,7 @@ struct PreparedCollation { validation_code_hash: ValidationCodeHash, n_validators: usize, core_index: CoreIndex, + session_index: SessionIndex, } /// Takes a prepared collation, along with its context, and produces a candidate receipt @@ -524,6 +554,8 @@ async fn construct_and_distribute_receipt( sender: &mut impl overseer::CollationGenerationSenderTrait, result_sender: Option>, metrics: &Metrics, + v2_receipts: bool, + transposed_claim_queue: &TransposedClaimQueue, ) { let PreparedCollation { collation, @@ -533,6 +565,7 @@ async fn construct_and_distribute_receipt( validation_code_hash, n_validators, core_index, + session_index, } = collation; let persisted_validation_data_hash = validation_data.hash(); @@ -596,25 +629,52 @@ async fn construct_and_distribute_receipt( hrmp_watermark: collation.hrmp_watermark, }; - let ccr = CandidateReceipt { - commitments_hash: commitments.hash(), - descriptor: CandidateDescriptor { - signature: key.sign(&signature_payload), - para_id, - relay_parent, - collator: key.public(), - persisted_validation_data_hash, - pov_hash, - erasure_root, - para_head: commitments.head_data.hash(), - validation_code_hash, + let receipt = if v2_receipts { + let ccr = CommittedCandidateReceiptV2 { + descriptor: CandidateDescriptorV2::new( + para_id, + relay_parent, + core_index, + session_index, + persisted_validation_data_hash, + pov_hash, + erasure_root, + commitments.head_data.hash(), + validation_code_hash, + ), + commitments, + }; + + if let Err(error) = ccr.check_core_index(transposed_claim_queue) { + gum::error!( + target: LOG_TARGET, + para_id = %para_id, + "V2 core index check failed: {:?}", error, + ); + } + + ccr.to_plain() + } else { + CandidateReceipt { + commitments_hash: commitments.hash(), + descriptor: CandidateDescriptor { + signature: key.sign(&signature_payload), + para_id, + relay_parent, + collator: key.public(), + persisted_validation_data_hash, + pov_hash, + erasure_root, + para_head: commitments.head_data.hash(), + validation_code_hash, + } + .into(), } - .into(), }; gum::debug!( target: LOG_TARGET, - candidate_hash = ?ccr.hash(), + candidate_hash = ?receipt.hash(), ?pov_hash, ?relay_parent, para_id = %para_id, @@ -624,7 +684,7 @@ async fn construct_and_distribute_receipt( sender .send_message(CollatorProtocolMessage::DistributeCollation { - candidate_receipt: ccr, + candidate_receipt: receipt, parent_head_data_hash, pov, parent_head_data, @@ -639,7 +699,7 @@ async fn obtain_validation_code_hash_with_assumption( para_id: ParaId, assumption: OccupiedCoreAssumption, sender: &mut impl overseer::CollationGenerationSenderTrait, -) -> crate::error::Result> { +) -> Result> { match request_validation_code_hash(relay_parent, para_id, assumption, sender) .await .await? @@ -665,7 +725,7 @@ fn erasure_root( n_validators: usize, persisted_validation: PersistedValidationData, pov: PoV, -) -> crate::error::Result { +) -> Result { let available_data = AvailableData { validation_data: persisted_validation, pov: Arc::new(pov) };