collation-generation + collator-protocol: collate on multiple assigned cores (#3795)

This works only for collators that implement the `collator_fn` allowing
`collation-generation` subsystem to pull collations triggered on new
heads.

Also enables
`request_v2::CollationFetchingResponse::CollationWithParentHeadData` for
test adder/undying collators.

TODO:
- [x] fix tests
- [x] new tests
- [x] PR doc

---------

Signed-off-by: Andrei Sandu <andrei-mihail@parity.io>
This commit is contained in:
Andrei Sandu
2024-03-27 16:44:10 +02:00
committed by GitHub
parent 25af0adf78
commit 417c54c61c
15 changed files with 556 additions and 140 deletions
@@ -49,7 +49,7 @@ use polkadot_node_subsystem::messages::{
CollationGenerationMessage, RuntimeApiMessage, RuntimeApiRequest, CollationGenerationMessage, RuntimeApiMessage, RuntimeApiRequest,
}; };
use polkadot_overseer::Handle as OverseerHandle; use polkadot_overseer::Handle as OverseerHandle;
use polkadot_primitives::{CollatorPair, Id as ParaId, OccupiedCoreAssumption}; use polkadot_primitives::{CollatorPair, CoreIndex, Id as ParaId, OccupiedCoreAssumption};
use futures::{channel::oneshot, prelude::*}; use futures::{channel::oneshot, prelude::*};
use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf}; use sc_client_api::{backend::AuxStore, BlockBackend, BlockOf};
@@ -184,7 +184,15 @@ where
while let Some(relay_parent_header) = import_notifications.next().await { while let Some(relay_parent_header) = import_notifications.next().await {
let relay_parent = relay_parent_header.hash(); let relay_parent = relay_parent_header.hash();
if !is_para_scheduled(relay_parent, params.para_id, &mut params.overseer_handle).await { // TODO: Currently we use just the first core here, but for elastic scaling
// we iterate and build on all of the cores returned.
let core_index = if let Some(core_index) =
cores_scheduled_for_para(relay_parent, params.para_id, &mut params.overseer_handle)
.await
.get(0)
{
*core_index
} else {
tracing::trace!( tracing::trace!(
target: crate::LOG_TARGET, target: crate::LOG_TARGET,
?relay_parent, ?relay_parent,
@@ -193,7 +201,7 @@ where
); );
continue continue
} };
let max_pov_size = match params let max_pov_size = match params
.relay_client .relay_client
@@ -396,6 +404,7 @@ where
parent_head: parent_header.encode().into(), parent_head: parent_header.encode().into(),
validation_code_hash, validation_code_hash,
result_sender: None, result_sender: None,
core_index,
}, },
), ),
"SubmitCollation", "SubmitCollation",
@@ -480,14 +489,12 @@ async fn max_ancestry_lookback(
} }
} }
// Checks if there exists a scheduled core for the para at the provided relay parent. // Return all the cores assigned to the para at the provided relay parent.
// async fn cores_scheduled_for_para(
// Falls back to `false` in case of an error.
async fn is_para_scheduled(
relay_parent: PHash, relay_parent: PHash,
para_id: ParaId, para_id: ParaId,
overseer_handle: &mut OverseerHandle, overseer_handle: &mut OverseerHandle,
) -> bool { ) -> Vec<CoreIndex> {
let (tx, rx) = oneshot::channel(); let (tx, rx) = oneshot::channel();
let request = RuntimeApiRequest::AvailabilityCores(tx); let request = RuntimeApiRequest::AvailabilityCores(tx);
overseer_handle overseer_handle
@@ -503,7 +510,7 @@ async fn is_para_scheduled(
?relay_parent, ?relay_parent,
"Failed to query availability cores runtime API", "Failed to query availability cores runtime API",
); );
return false return Vec::new()
}, },
Err(oneshot::Canceled) => { Err(oneshot::Canceled) => {
tracing::error!( tracing::error!(
@@ -511,9 +518,19 @@ async fn is_para_scheduled(
?relay_parent, ?relay_parent,
"Sender for availability cores runtime request dropped", "Sender for availability cores runtime request dropped",
); );
return false return Vec::new()
}, },
}; };
cores.iter().any(|core| core.para_id() == Some(para_id)) cores
.iter()
.enumerate()
.filter_map(|(index, core)| {
if core.para_id() == Some(para_id) {
Some(CoreIndex(index as u32))
} else {
None
}
})
.collect()
} }
@@ -28,6 +28,8 @@ pub enum Error {
Util(#[from] polkadot_node_subsystem_util::Error), Util(#[from] polkadot_node_subsystem_util::Error),
#[error(transparent)] #[error(transparent)]
Erasure(#[from] polkadot_erasure_coding::Error), Erasure(#[from] polkadot_erasure_coding::Error),
#[error("Parachain backing state not available in runtime.")]
MissingParaBackingState,
} }
pub type Result<T> = std::result::Result<T, Error>; pub type Result<T> = std::result::Result<T, Error>;
+108 -73
View File
@@ -44,8 +44,8 @@ use polkadot_node_subsystem::{
}; };
use polkadot_node_subsystem_util::{ use polkadot_node_subsystem_util::{
has_required_runtime, request_async_backing_params, request_availability_cores, has_required_runtime, request_async_backing_params, request_availability_cores,
request_claim_queue, request_persisted_validation_data, request_validation_code, request_claim_queue, request_para_backing_state, request_persisted_validation_data,
request_validation_code_hash, request_validators, request_validation_code, request_validation_code_hash, request_validators,
}; };
use polkadot_primitives::{ use polkadot_primitives::{
collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt, collator_signature_payload, CandidateCommitments, CandidateDescriptor, CandidateReceipt,
@@ -212,6 +212,7 @@ async fn handle_new_activations<Context>(
if config.collator.is_none() { if config.collator.is_none() {
return Ok(()) return Ok(())
} }
let para_id = config.para_id;
let _overall_timer = metrics.time_new_activations(); let _overall_timer = metrics.time_new_activations();
@@ -225,25 +226,23 @@ async fn handle_new_activations<Context>(
); );
let availability_cores = availability_cores??; let availability_cores = availability_cores??;
let n_validators = validators??.len();
let async_backing_params = async_backing_params?.ok(); 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?; let maybe_claim_queue = fetch_claim_queue(ctx.sender(), relay_parent).await?;
for (core_idx, core) in availability_cores.into_iter().enumerate() { // The loop bellow will fill in cores that the para is allowed to build on.
let _availability_core_timer = metrics.time_new_activations_availability_core(); let mut cores_to_build_on = Vec::new();
let (scheduled_core, assumption) = match core { for (core_idx, core) in availability_cores.into_iter().enumerate() {
CoreState::Scheduled(scheduled_core) => let scheduled_core = match core {
(scheduled_core, OccupiedCoreAssumption::Free), CoreState::Scheduled(scheduled_core) => scheduled_core,
CoreState::Occupied(occupied_core) => match async_backing_params { CoreState::Occupied(occupied_core) => match async_backing_params {
Some(params) if params.max_candidate_depth >= 1 => { Some(params) if params.max_candidate_depth >= 1 => {
// maximum candidate depth when building on top of a block // maximum candidate depth when building on top of a block
// pending availability is necessarily 1 - the depth of the // pending availability is necessarily 1 - the depth of the
// pending block is 0 so the child has depth 1. // pending block is 0 so the child has depth 1.
// TODO [now]: this assumes that next up == current. // Use claim queue if available, or fallback to `next_up_on_available`
// in practice we should only set `OccupiedCoreAssumption::Included`
// when the candidate occupying the core is also of the same para.
let res = match maybe_claim_queue { let res = match maybe_claim_queue {
Some(ref claim_queue) => { Some(ref claim_queue) => {
// read what's in the claim queue for this core // read what's in the claim queue for this core
@@ -257,8 +256,7 @@ async fn handle_new_activations<Context>(
// `next_up_on_available` // `next_up_on_available`
occupied_core.next_up_on_available occupied_core.next_up_on_available
}, },
} };
.map(|scheduled| (scheduled, OccupiedCoreAssumption::Included));
match res { match res {
Some(res) => res, Some(res) => res,
@@ -279,7 +277,7 @@ async fn handle_new_activations<Context>(
gum::trace!( gum::trace!(
target: LOG_TARGET, target: LOG_TARGET,
core_idx = %core_idx, core_idx = %core_idx,
"core is free. Keep going.", "core is not assigned to any para. Keep going.",
); );
continue continue
}, },
@@ -294,64 +292,90 @@ async fn handle_new_activations<Context>(
their_para = %scheduled_core.para_id, their_para = %scheduled_core.para_id,
"core is not assigned to our para. Keep going.", "core is not assigned to our para. Keep going.",
); );
continue } else {
// Accumulate cores for building collation(s) outside the loop.
cores_to_build_on.push(CoreIndex(core_idx as u32));
} }
}
// we get validation data and validation code synchronously for each core instead of // Skip to next relay parent if there is no core assigned to us.
// within the subtask loop, because we have only a single mutable handle to the if cores_to_build_on.is_empty() {
// context, so the work can't really be distributed continue
}
let validation_data = match request_persisted_validation_data( let para_backing_state =
relay_parent, request_para_backing_state(relay_parent, config.para_id, ctx.sender())
scheduled_core.para_id, .await
assumption, .await??
ctx.sender(), .ok_or(crate::error::Error::MissingParaBackingState)?;
)
.await
.await??
{
Some(v) => v,
None => {
gum::trace!(
target: LOG_TARGET,
core_idx = %core_idx,
relay_parent = ?relay_parent,
our_para = %config.para_id,
their_para = %scheduled_core.para_id,
"validation data is not available",
);
continue
},
};
let validation_code_hash = match obtain_validation_code_hash_with_assumption( // We are being very optimistic here, but one of the cores could pend availability some more
relay_parent, // block, ore even time out.
scheduled_core.para_id, // For timeout assumption the collator can't really know because it doesn't receive bitfield
assumption, // gossip.
ctx.sender(), let assumption = if para_backing_state.pending_availability.is_empty() {
) OccupiedCoreAssumption::Free
.await? } else {
{ OccupiedCoreAssumption::Included
Some(v) => v, };
None => {
gum::trace!(
target: LOG_TARGET,
core_idx = %core_idx,
relay_parent = ?relay_parent,
our_para = %config.para_id,
their_para = %scheduled_core.para_id,
"validation code hash is not found.",
);
continue
},
};
let task_config = config.clone(); gum::debug!(
let metrics = metrics.clone(); target: LOG_TARGET,
let mut task_sender = ctx.sender().clone(); relay_parent = ?relay_parent,
ctx.spawn( our_para = %config.para_id,
"collation-builder", ?assumption,
Box::pin(async move { "Occupied core(s) assumption",
);
let mut validation_data = match request_persisted_validation_data(
relay_parent,
config.para_id,
assumption,
ctx.sender(),
)
.await
.await??
{
Some(v) => v,
None => {
gum::debug!(
target: LOG_TARGET,
relay_parent = ?relay_parent,
our_para = %config.para_id,
"validation data is not available",
);
continue
},
};
let validation_code_hash = match obtain_validation_code_hash_with_assumption(
relay_parent,
config.para_id,
assumption,
ctx.sender(),
)
.await?
{
Some(v) => v,
None => {
gum::debug!(
target: LOG_TARGET,
relay_parent = ?relay_parent,
our_para = %config.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() { let collator_fn = match task_config.collator.as_ref() {
Some(x) => x, Some(x) => x,
None => return, None => return,
@@ -363,21 +387,23 @@ async fn handle_new_activations<Context>(
None => { None => {
gum::debug!( gum::debug!(
target: LOG_TARGET, target: LOG_TARGET,
para_id = %scheduled_core.para_id, ?para_id,
"collator returned no collation on collate", "collator returned no collation on collate",
); );
return return
}, },
}; };
let parent_head = collation.head_data.clone();
construct_and_distribute_receipt( construct_and_distribute_receipt(
PreparedCollation { PreparedCollation {
collation, collation,
para_id: scheduled_core.para_id, para_id,
relay_parent, relay_parent,
validation_data, validation_data: validation_data.clone(),
validation_code_hash, validation_code_hash,
n_validators, n_validators,
core_index,
}, },
task_config.key.clone(), task_config.key.clone(),
&mut task_sender, &mut task_sender,
@@ -385,9 +411,13 @@ async fn handle_new_activations<Context>(
&metrics, &metrics,
) )
.await; .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(())
@@ -408,6 +438,7 @@ async fn handle_submit_collation<Context>(
parent_head, parent_head,
validation_code_hash, validation_code_hash,
result_sender, result_sender,
core_index,
} = params; } = params;
let validators = request_validators(relay_parent, ctx.sender()).await.await??; let validators = request_validators(relay_parent, ctx.sender()).await.await??;
@@ -444,6 +475,7 @@ async fn handle_submit_collation<Context>(
validation_data, validation_data,
validation_code_hash, validation_code_hash,
n_validators, n_validators,
core_index,
}; };
construct_and_distribute_receipt( construct_and_distribute_receipt(
@@ -465,6 +497,7 @@ struct PreparedCollation {
validation_data: PersistedValidationData, validation_data: PersistedValidationData,
validation_code_hash: ValidationCodeHash, validation_code_hash: ValidationCodeHash,
n_validators: usize, n_validators: usize,
core_index: CoreIndex,
} }
/// Takes a prepared collation, along with its context, and produces a candidate receipt /// Takes a prepared collation, along with its context, and produces a candidate receipt
@@ -483,6 +516,7 @@ async fn construct_and_distribute_receipt(
validation_data, validation_data,
validation_code_hash, validation_code_hash,
n_validators, n_validators,
core_index,
} = collation; } = collation;
let persisted_validation_data_hash = validation_data.hash(); let persisted_validation_data_hash = validation_data.hash();
@@ -578,6 +612,7 @@ async fn construct_and_distribute_receipt(
pov, pov,
parent_head_data, parent_head_data,
result_sender, result_sender,
core_index,
}) })
.await; .await;
} }
+256 -5
View File
@@ -30,13 +30,16 @@ use polkadot_node_subsystem::{
use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle}; use polkadot_node_subsystem_test_helpers::{subsystem_test_harness, TestSubsystemContextHandle};
use polkadot_node_subsystem_util::TimeoutExt; use polkadot_node_subsystem_util::TimeoutExt;
use polkadot_primitives::{ use polkadot_primitives::{
AsyncBackingParams, CollatorPair, HeadData, Id as ParaId, Id, PersistedValidationData, async_backing::{BackingState, CandidatePendingAvailability},
AsyncBackingParams, BlockNumber, CollatorPair, HeadData, PersistedValidationData,
ScheduledCore, ValidationCode, ScheduledCore, ValidationCode,
}; };
use rstest::rstest; use rstest::rstest;
use sp_keyring::sr25519::Keyring as Sr25519Keyring; use sp_keyring::sr25519::Keyring as Sr25519Keyring;
use std::pin::Pin; use std::pin::Pin;
use test_helpers::{dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator}; use test_helpers::{
dummy_candidate_descriptor, dummy_hash, dummy_head_data, dummy_validator, make_candidate,
};
type VirtualOverseer = TestSubsystemContextHandle<CollationGenerationMessage>; type VirtualOverseer = TestSubsystemContextHandle<CollationGenerationMessage>;
@@ -105,9 +108,9 @@ impl Future for TestCollator {
impl Unpin for TestCollator {} impl Unpin for TestCollator {}
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages { const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(2000);
const TIMEOUT: std::time::Duration = std::time::Duration::from_millis(2000);
async fn overseer_recv(overseer: &mut VirtualOverseer) -> AllMessages {
overseer overseer
.recv() .recv()
.timeout(TIMEOUT) .timeout(TIMEOUT)
@@ -135,6 +138,41 @@ fn scheduled_core_for<Id: Into<ParaId>>(para_id: Id) -> ScheduledCore {
ScheduledCore { para_id: para_id.into(), collator: None } ScheduledCore { para_id: para_id.into(), collator: None }
} }
fn dummy_candidate_pending_availability(
para_id: ParaId,
candidate_relay_parent: Hash,
relay_parent_number: BlockNumber,
) -> CandidatePendingAvailability {
let (candidate, _pvd) = make_candidate(
candidate_relay_parent,
relay_parent_number,
para_id,
dummy_head_data(),
HeadData(vec![1]),
ValidationCode(vec![1, 2, 3]).hash(),
);
let candidate_hash = candidate.hash();
CandidatePendingAvailability {
candidate_hash,
descriptor: candidate.descriptor,
commitments: candidate.commitments,
relay_parent_number,
max_pov_size: 5 * 1024 * 1024,
}
}
fn dummy_backing_state(pending_availability: Vec<CandidatePendingAvailability>) -> BackingState {
let constraints = helpers::dummy_constraints(
0,
vec![0],
dummy_head_data(),
ValidationCodeHash::from(Hash::repeat_byte(42)),
);
BackingState { constraints, pending_availability }
}
#[rstest] #[rstest]
#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)] #[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT - 1)]
#[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)] #[case(RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT)]
@@ -176,6 +214,12 @@ fn requests_availability_per_relay_parent(#[case] runtime_version: u32) {
))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => {
tx.send(Ok(BTreeMap::new())).unwrap(); tx.send(Ok(BTreeMap::new())).unwrap();
}, },
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::ParaBackingState(_para_id, tx),
))) => {
tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap();
},
Some(msg) => panic!("didn't expect any other overseer requests given no availability cores; got {:?}", msg), Some(msg) => panic!("didn't expect any other overseer requests given no availability cores; got {:?}", msg),
} }
} }
@@ -273,6 +317,12 @@ fn requests_validation_data_for_scheduled_matches(#[case] runtime_version: u32)
))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => {
tx.send(Ok(BTreeMap::new())).unwrap(); tx.send(Ok(BTreeMap::new())).unwrap();
}, },
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::ParaBackingState(_para_id, tx),
))) => {
tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap();
},
Some(msg) => { Some(msg) => {
panic!("didn't expect any other overseer requests; got {:?}", msg) panic!("didn't expect any other overseer requests; got {:?}", msg)
}, },
@@ -384,6 +434,12 @@ fn sends_distribute_collation_message(#[case] runtime_version: u32) {
))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => { ))) if runtime_version >= RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT => {
tx.send(Ok(BTreeMap::new())).unwrap(); tx.send(Ok(BTreeMap::new())).unwrap();
}, },
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::ParaBackingState(_para_id, tx),
))) => {
tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap();
},
Some(msg @ AllMessages::CollatorProtocol(_)) => { Some(msg @ AllMessages::CollatorProtocol(_)) => {
inner_to_collator_protocol.lock().await.push(msg); inner_to_collator_protocol.lock().await.push(msg);
}, },
@@ -564,6 +620,12 @@ fn fallback_when_no_validation_code_hash_api(#[case] runtime_version: u32) {
let res = BTreeMap::<CoreIndex, VecDeque<ParaId>>::new(); let res = BTreeMap::<CoreIndex, VecDeque<ParaId>>::new();
tx.send(Ok(res)).unwrap(); tx.send(Ok(res)).unwrap();
}, },
Some(AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_hash,
RuntimeApiRequest::ParaBackingState(_para_id, tx),
))) => {
tx.send(Ok(Some(dummy_backing_state(vec![])))).unwrap();
},
Some(msg) => { Some(msg) => {
panic!("didn't expect any other overseer requests; got {:?}", msg) panic!("didn't expect any other overseer requests; got {:?}", msg)
}, },
@@ -611,6 +673,7 @@ fn submit_collation_is_no_op_before_initialization() {
parent_head: vec![1, 2, 3].into(), parent_head: vec![1, 2, 3].into(),
validation_code_hash: Hash::repeat_byte(1).into(), validation_code_hash: Hash::repeat_byte(1).into(),
result_sender: None, result_sender: None,
core_index: CoreIndex(0),
}), }),
}) })
.await; .await;
@@ -647,6 +710,7 @@ fn submit_collation_leads_to_distribution() {
parent_head: vec![1, 2, 3].into(), parent_head: vec![1, 2, 3].into(),
validation_code_hash, validation_code_hash,
result_sender: None, result_sender: None,
core_index: CoreIndex(0),
}), }),
}) })
.await; .await;
@@ -721,6 +785,9 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run
test_harness(|mut virtual_overseer| async move { test_harness(|mut virtual_overseer| async move {
helpers::initialize_collator(&mut virtual_overseer, para_id).await; helpers::initialize_collator(&mut virtual_overseer, para_id).await;
helpers::activate_new_head(&mut virtual_overseer, activated_hash).await; helpers::activate_new_head(&mut virtual_overseer, activated_hash).await;
let pending_availability =
vec![dummy_candidate_pending_availability(para_id, activated_hash, 1)];
helpers::handle_runtime_calls_on_new_head_activation( helpers::handle_runtime_calls_on_new_head_activation(
&mut virtual_overseer, &mut virtual_overseer,
activated_hash, activated_hash,
@@ -728,6 +795,7 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run
cores, cores,
runtime_version, runtime_version,
claim_queue, claim_queue,
pending_availability,
) )
.await; .await;
helpers::handle_core_processing_for_a_leaf( helpers::handle_core_processing_for_a_leaf(
@@ -736,6 +804,131 @@ fn distribute_collation_for_occupied_core_with_async_backing_enabled(#[case] run
para_id, para_id,
// `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included` // `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included`
OccupiedCoreAssumption::Included, OccupiedCoreAssumption::Included,
1,
)
.await;
virtual_overseer
});
}
// There are variable number of cores of cores in `Occupied` state and async backing is enabled.
// On new head activation `CollationGeneration` should produce and distribute a new collation
// with proper assumption about the para candidate chain availability at next block.
#[rstest]
#[case(0)]
#[case(1)]
#[case(2)]
fn distribute_collation_for_occupied_cores_with_async_backing_enabled_and_elastic_scaling(
#[case] candidates_pending_avail: u32,
) {
let activated_hash: Hash = [1; 32].into();
let para_id = ParaId::from(5);
let cores = (0..candidates_pending_avail)
.into_iter()
.map(|idx| {
CoreState::Occupied(polkadot_primitives::OccupiedCore {
next_up_on_available: Some(ScheduledCore { para_id, collator: None }),
occupied_since: 0,
time_out_at: 10,
next_up_on_time_out: Some(ScheduledCore { para_id, collator: None }),
availability: Default::default(), // doesn't matter
group_responsible: polkadot_primitives::GroupIndex(idx as u32),
candidate_hash: Default::default(),
candidate_descriptor: dummy_candidate_descriptor(dummy_hash()),
})
})
.collect::<Vec<_>>();
let pending_availability = (0..candidates_pending_avail)
.into_iter()
.map(|_idx| dummy_candidate_pending_availability(para_id, activated_hash, 0))
.collect::<Vec<_>>();
let claim_queue = cores
.iter()
.enumerate()
.map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id])))
.collect::<BTreeMap<_, _>>();
let total_cores = cores.len();
test_harness(|mut virtual_overseer| async move {
helpers::initialize_collator(&mut virtual_overseer, para_id).await;
helpers::activate_new_head(&mut virtual_overseer, activated_hash).await;
helpers::handle_runtime_calls_on_new_head_activation(
&mut virtual_overseer,
activated_hash,
AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 },
cores,
// Using latest runtime with the fancy claim queue exposed.
RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT,
claim_queue,
pending_availability,
)
.await;
helpers::handle_core_processing_for_a_leaf(
&mut virtual_overseer,
activated_hash,
para_id,
// `CoreState` is `Occupied` => `OccupiedCoreAssumption` is `Included`
OccupiedCoreAssumption::Included,
total_cores,
)
.await;
virtual_overseer
});
}
// There are variable number of cores of cores in `Free` state and async backing is enabled.
// On new head activation `CollationGeneration` should produce and distribute a new collation
// with proper assumption about the para candidate chain availability at next block.
#[rstest]
#[case(0)]
#[case(1)]
#[case(2)]
fn distribute_collation_for_free_cores_with_async_backing_enabled_and_elastic_scaling(
#[case] candidates_pending_avail: u32,
) {
let activated_hash: Hash = [1; 32].into();
let para_id = ParaId::from(5);
let cores = (0..candidates_pending_avail)
.into_iter()
.map(|_idx| CoreState::Scheduled(ScheduledCore { para_id, collator: None }))
.collect::<Vec<_>>();
let claim_queue = cores
.iter()
.enumerate()
.map(|(idx, _core)| (CoreIndex::from(idx as u32), VecDeque::from([para_id])))
.collect::<BTreeMap<_, _>>();
let total_cores = cores.len();
test_harness(|mut virtual_overseer| async move {
helpers::initialize_collator(&mut virtual_overseer, para_id).await;
helpers::activate_new_head(&mut virtual_overseer, activated_hash).await;
helpers::handle_runtime_calls_on_new_head_activation(
&mut virtual_overseer,
activated_hash,
AsyncBackingParams { max_candidate_depth: 1, allowed_ancestry_len: 1 },
cores,
// Using latest runtime with the fancy claim queue exposed.
RuntimeApiRequest::CLAIM_QUEUE_RUNTIME_REQUIREMENT,
claim_queue,
vec![],
)
.await;
helpers::handle_core_processing_for_a_leaf(
&mut virtual_overseer,
activated_hash,
para_id,
// `CoreState` is `Free` => `OccupiedCoreAssumption` is `Free`
OccupiedCoreAssumption::Free,
total_cores,
) )
.await; .await;
@@ -777,6 +970,7 @@ fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled(
cores, cores,
runtime_version, runtime_version,
claim_queue, claim_queue,
vec![],
) )
.await; .await;
@@ -785,8 +979,38 @@ fn no_collation_is_distributed_for_occupied_core_with_async_backing_disabled(
} }
mod helpers { mod helpers {
use polkadot_primitives::{
async_backing::{Constraints, InboundHrmpLimitations},
BlockNumber,
};
use super::*; use super::*;
// A set for dummy constraints for `ParaBackingState``
pub(crate) fn dummy_constraints(
min_relay_parent_number: BlockNumber,
valid_watermarks: Vec<BlockNumber>,
required_parent: HeadData,
validation_code_hash: ValidationCodeHash,
) -> Constraints {
Constraints {
min_relay_parent_number,
max_pov_size: 5 * 1024 * 1024,
max_code_size: 1_000_000,
ump_remaining: 10,
ump_remaining_bytes: 1_000,
max_ump_num_per_candidate: 10,
dmp_remaining_messages: vec![],
hrmp_inbound: InboundHrmpLimitations { valid_watermarks },
hrmp_channels_out: vec![],
max_hrmp_num_per_candidate: 0,
required_parent,
validation_code_hash,
upgrade_restriction: None,
future_validation_code: None,
}
}
// Sends `Initialize` with a collator config // Sends `Initialize` with a collator config
pub async fn initialize_collator(virtual_overseer: &mut VirtualOverseer, para_id: ParaId) { pub async fn initialize_collator(virtual_overseer: &mut VirtualOverseer, para_id: ParaId) {
virtual_overseer virtual_overseer
@@ -822,7 +1046,8 @@ mod helpers {
async_backing_params: AsyncBackingParams, async_backing_params: AsyncBackingParams,
cores: Vec<CoreState>, cores: Vec<CoreState>,
runtime_version: u32, runtime_version: u32,
claim_queue: BTreeMap<CoreIndex, VecDeque<Id>>, claim_queue: BTreeMap<CoreIndex, VecDeque<ParaId>>,
pending_availability: Vec<CandidatePendingAvailability>,
) { ) {
assert_matches!( assert_matches!(
overseer_recv(virtual_overseer).await, overseer_recv(virtual_overseer).await,
@@ -857,6 +1082,25 @@ mod helpers {
} }
); );
// Process the `ParaBackingState` message, and return some dummy state.
let message = overseer_recv(virtual_overseer).await;
let para_id = match message {
AllMessages::RuntimeApi(RuntimeApiMessage::Request(
_,
RuntimeApiRequest::ParaBackingState(p_id, _),
)) => p_id,
_ => panic!("received unexpected message {:?}", message),
};
assert_matches!(
message,
AllMessages::RuntimeApi(
RuntimeApiMessage::Request(parent, RuntimeApiRequest::ParaBackingState(p_id, tx))
) if parent == activated_hash && p_id == para_id => {
tx.send(Ok(Some(dummy_backing_state(pending_availability)))).unwrap();
}
);
assert_matches!( assert_matches!(
overseer_recv(virtual_overseer).await, overseer_recv(virtual_overseer).await,
AllMessages::RuntimeApi(RuntimeApiMessage::Request( AllMessages::RuntimeApi(RuntimeApiMessage::Request(
@@ -889,7 +1133,14 @@ mod helpers {
activated_hash: Hash, activated_hash: Hash,
para_id: ParaId, para_id: ParaId,
expected_occupied_core_assumption: OccupiedCoreAssumption, expected_occupied_core_assumption: OccupiedCoreAssumption,
cores_assigned: usize,
) { ) {
// Expect no messages if no cores is assigned to the para
if cores_assigned == 0 {
assert!(overseer_recv(virtual_overseer).timeout(TIMEOUT / 2).await.is_none());
return
}
// Some hardcoded data - if needed, extract to parameters // Some hardcoded data - if needed, extract to parameters
let validation_code_hash = ValidationCodeHash::from(Hash::repeat_byte(42)); let validation_code_hash = ValidationCodeHash::from(Hash::repeat_byte(42));
let parent_head = HeadData::from(vec![1, 2, 3]); let parent_head = HeadData::from(vec![1, 2, 3]);
@@ -203,20 +203,40 @@ struct PeerData {
version: CollationVersion, version: CollationVersion,
} }
/// A type wrapping a collation and it's designated core index.
struct CollationWithCoreIndex(Collation, CoreIndex);
impl CollationWithCoreIndex {
/// Returns inner collation ref.
pub fn collation(&self) -> &Collation {
&self.0
}
/// Returns inner collation mut ref.
pub fn collation_mut(&mut self) -> &mut Collation {
&mut self.0
}
/// Returns inner core index.
pub fn core_index(&self) -> &CoreIndex {
&self.1
}
}
struct PerRelayParent { struct PerRelayParent {
prospective_parachains_mode: ProspectiveParachainsMode, prospective_parachains_mode: ProspectiveParachainsMode,
/// Validators group responsible for backing candidates built /// Per core index validators group responsible for backing candidates built
/// on top of this relay parent. /// on top of this relay parent.
validator_group: ValidatorGroup, validator_group: HashMap<CoreIndex, ValidatorGroup>,
/// Distributed collations. /// Distributed collations.
collations: HashMap<CandidateHash, Collation>, collations: HashMap<CandidateHash, CollationWithCoreIndex>,
} }
impl PerRelayParent { impl PerRelayParent {
fn new(mode: ProspectiveParachainsMode) -> Self { fn new(mode: ProspectiveParachainsMode) -> Self {
Self { Self {
prospective_parachains_mode: mode, prospective_parachains_mode: mode,
validator_group: ValidatorGroup::default(), validator_group: HashMap::default(),
collations: HashMap::new(), collations: HashMap::new(),
} }
} }
@@ -350,6 +370,7 @@ async fn distribute_collation<Context>(
pov: PoV, pov: PoV,
parent_head_data: HeadData, parent_head_data: HeadData,
result_sender: Option<oneshot::Sender<CollationSecondedSignal>>, result_sender: Option<oneshot::Sender<CollationSecondedSignal>>,
core_index: CoreIndex,
) -> Result<()> { ) -> Result<()> {
let candidate_relay_parent = receipt.descriptor.relay_parent; let candidate_relay_parent = receipt.descriptor.relay_parent;
let candidate_hash = receipt.hash(); let candidate_hash = receipt.hash();
@@ -422,7 +443,22 @@ async fn distribute_collation<Context>(
); );
} }
let our_core = our_cores[0]; // Double check that the specified `core_index` is among the ones our para has assignments for.
if !our_cores.iter().any(|assigned_core| assigned_core == &core_index) {
gum::warn!(
target: LOG_TARGET,
para_id = %id,
relay_parent = ?candidate_relay_parent,
cores = ?our_cores,
?core_index,
"Attempting to distribute collation for a core we are not assigned to ",
);
return Ok(())
}
let our_core = core_index;
// Determine the group on that core. // Determine the group on that core.
// //
// When prospective parachains are disabled, candidate relay parent here is // When prospective parachains are disabled, candidate relay parent here is
@@ -464,10 +500,12 @@ async fn distribute_collation<Context>(
"Accepted collation, connecting to validators." "Accepted collation, connecting to validators."
); );
let validators_at_relay_parent = &mut per_relay_parent.validator_group.validators; // Insert validator group for the `core_index` at relay parent.
if validators_at_relay_parent.is_empty() { per_relay_parent.validator_group.entry(core_index).or_insert_with(|| {
*validators_at_relay_parent = validators; let mut group = ValidatorGroup::default();
} group.validators = validators;
group
});
// Update a set of connected validators if necessary. // Update a set of connected validators if necessary.
connect_to_validators(ctx, &state.validator_groups_buf).await; connect_to_validators(ctx, &state.validator_groups_buf).await;
@@ -484,7 +522,10 @@ async fn distribute_collation<Context>(
per_relay_parent.collations.insert( per_relay_parent.collations.insert(
candidate_hash, candidate_hash,
Collation { receipt, pov, parent_head_data, status: CollationStatus::Created }, CollationWithCoreIndex(
Collation { receipt, pov, parent_head_data, status: CollationStatus::Created },
core_index,
),
); );
// If prospective parachains are disabled, a leaf should be known to peer. // If prospective parachains are disabled, a leaf should be known to peer.
@@ -690,7 +731,10 @@ async fn advertise_collation<Context>(
advertisement_timeouts: &mut FuturesUnordered<ResetInterestTimeout>, advertisement_timeouts: &mut FuturesUnordered<ResetInterestTimeout>,
metrics: &Metrics, metrics: &Metrics,
) { ) {
for (candidate_hash, collation) in per_relay_parent.collations.iter_mut() { for (candidate_hash, collation_and_core) in per_relay_parent.collations.iter_mut() {
let core_index = *collation_and_core.core_index();
let collation = collation_and_core.collation_mut();
// Check that peer will be able to request the collation. // Check that peer will be able to request the collation.
if let CollationVersion::V1 = protocol_version { if let CollationVersion::V1 = protocol_version {
if per_relay_parent.prospective_parachains_mode.is_enabled() { if per_relay_parent.prospective_parachains_mode.is_enabled() {
@@ -704,11 +748,17 @@ async fn advertise_collation<Context>(
} }
} }
let should_advertise = let Some(validator_group) = per_relay_parent.validator_group.get_mut(&core_index) else {
per_relay_parent gum::debug!(
.validator_group target: LOG_TARGET,
.should_advertise_to(candidate_hash, peer_ids, &peer); ?relay_parent,
?core_index,
"Skipping advertising to validator, validator group for core not found",
);
return
};
let should_advertise = validator_group.should_advertise_to(candidate_hash, peer_ids, &peer);
match should_advertise { match should_advertise {
ShouldAdvertiseTo::Yes => {}, ShouldAdvertiseTo::Yes => {},
ShouldAdvertiseTo::NotAuthority | ShouldAdvertiseTo::AlreadyAdvertised => { ShouldAdvertiseTo::NotAuthority | ShouldAdvertiseTo::AlreadyAdvertised => {
@@ -756,9 +806,7 @@ async fn advertise_collation<Context>(
)) ))
.await; .await;
per_relay_parent validator_group.advertised_to_peer(candidate_hash, &peer_ids, peer);
.validator_group
.advertised_to_peer(candidate_hash, &peer_ids, peer);
advertisement_timeouts.push(ResetInterestTimeout::new( advertisement_timeouts.push(ResetInterestTimeout::new(
*candidate_hash, *candidate_hash,
@@ -790,6 +838,7 @@ async fn process_msg<Context>(
pov, pov,
parent_head_data, parent_head_data,
result_sender, result_sender,
core_index,
} => { } => {
let _span1 = state let _span1 = state
.span_per_relay_parent .span_per_relay_parent
@@ -820,6 +869,7 @@ async fn process_msg<Context>(
pov, pov,
parent_head_data, parent_head_data,
result_sender, result_sender,
core_index,
) )
.await?; .await?;
}, },
@@ -1053,7 +1103,7 @@ async fn handle_incoming_request<Context>(
}; };
let mode = per_relay_parent.prospective_parachains_mode; let mode = per_relay_parent.prospective_parachains_mode;
let collation = match &req { let collation_with_core = match &req {
VersionedCollationRequest::V1(_) if !mode.is_enabled() => VersionedCollationRequest::V1(_) if !mode.is_enabled() =>
per_relay_parent.collations.values_mut().next(), per_relay_parent.collations.values_mut().next(),
VersionedCollationRequest::V2(req) => VersionedCollationRequest::V2(req) =>
@@ -1070,22 +1120,24 @@ async fn handle_incoming_request<Context>(
return Ok(()) return Ok(())
}, },
}; };
let (receipt, pov, parent_head_data) = if let Some(collation) = collation { let (receipt, pov, parent_head_data) =
collation.status.advance_to_requested(); if let Some(collation_with_core) = collation_with_core {
( let collation = collation_with_core.collation_mut();
collation.receipt.clone(), collation.status.advance_to_requested();
collation.pov.clone(), (
collation.parent_head_data.clone(), collation.receipt.clone(),
) collation.pov.clone(),
} else { collation.parent_head_data.clone(),
gum::warn!( )
target: LOG_TARGET, } else {
relay_parent = %relay_parent, gum::warn!(
"received a `RequestCollation` for a relay parent we don't have collation stored.", target: LOG_TARGET,
); relay_parent = %relay_parent,
"received a `RequestCollation` for a relay parent we don't have collation stored.",
);
return Ok(()) return Ok(())
}; };
state.metrics.on_collation_sent_requested(); state.metrics.on_collation_sent_requested();
@@ -1340,7 +1392,9 @@ where
.remove(removed) .remove(removed)
.map(|per_relay_parent| per_relay_parent.collations) .map(|per_relay_parent| per_relay_parent.collations)
.unwrap_or_default(); .unwrap_or_default();
for collation in collations.into_values() { for collation_with_core in collations.into_values() {
let collation = collation_with_core.collation();
let candidate_hash = collation.receipt.hash(); let candidate_hash = collation.receipt.hash();
state.collation_result_senders.remove(&candidate_hash); state.collation_result_senders.remove(&candidate_hash);
state.validator_groups_buf.remove_candidate(&candidate_hash); state.validator_groups_buf.remove_candidate(&candidate_hash);
@@ -1477,7 +1531,7 @@ async fn run_inner<Context>(
continue continue
}; };
let next_collation = { let next_collation_with_core = {
let per_relay_parent = match state.per_relay_parent.get(&relay_parent) { let per_relay_parent = match state.per_relay_parent.get(&relay_parent) {
Some(per_relay_parent) => per_relay_parent, Some(per_relay_parent) => per_relay_parent,
None => continue, None => continue,
@@ -1497,7 +1551,8 @@ async fn run_inner<Context>(
} }
}; };
if let Some(collation) = next_collation { if let Some(collation_with_core) = next_collation_with_core {
let collation = collation_with_core.collation();
let receipt = collation.receipt.clone(); let receipt = collation.receipt.clone();
let pov = collation.pov.clone(); let pov = collation.pov.clone();
let parent_head_data = collation.parent_head_data.clone(); let parent_head_data = collation.parent_head_data.clone();
@@ -377,6 +377,7 @@ async fn distribute_collation_with_receipt(
pov: pov.clone(), pov: pov.clone(),
parent_head_data: HeadData(vec![1, 2, 3]), parent_head_data: HeadData(vec![1, 2, 3]),
result_sender: None, result_sender: None,
core_index: CoreIndex(0),
}, },
) )
.await; .await;
@@ -277,6 +277,7 @@ fn distribute_collation_from_implicit_view() {
pov: pov.clone(), pov: pov.clone(),
parent_head_data: HeadData(vec![1, 2, 3]), parent_head_data: HeadData(vec![1, 2, 3]),
result_sender: None, result_sender: None,
core_index: CoreIndex(0),
}, },
) )
.await; .await;
@@ -358,6 +359,7 @@ fn distribute_collation_up_to_limit() {
pov: pov.clone(), pov: pov.clone(),
parent_head_data: HeadData(vec![1, 2, 3]), parent_head_data: HeadData(vec![1, 2, 3]),
result_sender: None, result_sender: None,
core_index: CoreIndex(0),
}, },
) )
.await; .await;
@@ -45,14 +45,22 @@ use futures::FutureExt;
use polkadot_node_network_protocol::PeerId; use polkadot_node_network_protocol::PeerId;
use polkadot_primitives::{AuthorityDiscoveryId, CandidateHash, GroupIndex, SessionIndex}; use polkadot_primitives::{AuthorityDiscoveryId, CandidateHash, GroupIndex, SessionIndex};
/// Elastic scaling: how many candidates per relay chain block the collator supports building.
pub const MAX_CHAINED_CANDIDATES_PER_RCB: NonZeroUsize = match NonZeroUsize::new(3) {
Some(cap) => cap,
None => panic!("max candidates per rcb cannot be zero"),
};
/// The ring buffer stores at most this many unique validator groups. /// The ring buffer stores at most this many unique validator groups.
/// ///
/// This value should be chosen in way that all groups assigned to our para /// This value should be chosen in way that all groups assigned to our para
/// in the view can fit into the buffer. /// in the view can fit into the buffer multiplied by amount of candidates we support per relay
pub const VALIDATORS_BUFFER_CAPACITY: NonZeroUsize = match NonZeroUsize::new(3) { /// chain block in the case of elastic scaling.
Some(cap) => cap, pub const VALIDATORS_BUFFER_CAPACITY: NonZeroUsize =
None => panic!("buffer capacity must be non-zero"), match NonZeroUsize::new(3 * MAX_CHAINED_CANDIDATES_PER_RCB.get()) {
}; Some(cap) => cap,
None => panic!("buffer capacity must be non-zero"),
};
/// Unique identifier of a validators group. /// Unique identifier of a validators group.
#[derive(Debug)] #[derive(Debug)]
+4 -2
View File
@@ -31,8 +31,8 @@ use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use polkadot_primitives::{ use polkadot_primitives::{
BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, CollatorPair, BlakeTwo256, BlockNumber, CandidateCommitments, CandidateHash, CollatorPair,
CommittedCandidateReceipt, CompactStatement, EncodeAs, Hash, HashT, HeadData, Id as ParaId, CommittedCandidateReceipt, CompactStatement, CoreIndex, EncodeAs, Hash, HashT, HeadData,
PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode, Id as ParaId, PersistedValidationData, SessionIndex, Signed, UncheckedSigned, ValidationCode,
ValidationCodeHash, ValidatorIndex, MAX_CODE_SIZE, MAX_POV_SIZE, ValidationCodeHash, ValidatorIndex, MAX_CODE_SIZE, MAX_POV_SIZE,
}; };
pub use sp_consensus_babe::{ pub use sp_consensus_babe::{
@@ -524,6 +524,8 @@ pub struct SubmitCollationParams {
/// okay to just drop it. However, if it is called, it should be called with the signed /// okay to just drop it. However, if it is called, it should be called with the signed
/// statement of a parachain validator seconding the collation. /// statement of a parachain validator seconding the collation.
pub result_sender: Option<futures::channel::oneshot::Sender<CollationSecondedSignal>>, pub result_sender: Option<futures::channel::oneshot::Sender<CollationSecondedSignal>>,
/// The core index on which the resulting candidate should be backed
pub core_index: CoreIndex,
} }
/// This is the data we keep available for each candidate included in the relay chain. /// This is the data we keep available for each candidate included in the relay chain.
@@ -228,6 +228,8 @@ pub enum CollatorProtocolMessage {
/// The result sender should be informed when at least one parachain validator seconded the /// The result sender should be informed when at least one parachain validator seconded the
/// collation. It is also completely okay to just drop the sender. /// collation. It is also completely okay to just drop the sender.
result_sender: Option<oneshot::Sender<CollationSecondedSignal>>, result_sender: Option<oneshot::Sender<CollationSecondedSignal>>,
/// The core index where the candidate should be backed.
core_index: CoreIndex,
}, },
/// Report a collator as having provided an invalid collation. This should lead to disconnect /// Report a collator as having provided an invalid collation. This should lead to disconnect
/// and blacklist of the collator. /// and blacklist of the collator.
+2 -1
View File
@@ -30,7 +30,7 @@ use polkadot_node_subsystem::{
messages::{RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender}, messages::{RuntimeApiMessage, RuntimeApiRequest, RuntimeApiSender},
overseer, SubsystemSender, overseer, SubsystemSender,
}; };
use polkadot_primitives::{slashing, CoreIndex, ExecutorParams}; use polkadot_primitives::{async_backing::BackingState, slashing, CoreIndex, ExecutorParams};
pub use overseer::{ pub use overseer::{
gen::{OrchestraError as OverseerError, Timeout}, gen::{OrchestraError as OverseerError, Timeout},
@@ -308,6 +308,7 @@ specialize_requests! {
fn request_disabled_validators() -> Vec<ValidatorIndex>; DisabledValidators; fn request_disabled_validators() -> Vec<ValidatorIndex>; DisabledValidators;
fn request_async_backing_params() -> AsyncBackingParams; AsyncBackingParams; fn request_async_backing_params() -> AsyncBackingParams; AsyncBackingParams;
fn request_claim_queue() -> BTreeMap<CoreIndex, VecDeque<ParaId>>; ClaimQueue; fn request_claim_queue() -> BTreeMap<CoreIndex, VecDeque<ParaId>>; ClaimQueue;
fn request_para_backing_state(para_id: ParaId) -> Option<BackingState>; ParaBackingState;
} }
/// Requests executor parameters from the runtime effective at given relay-parent. First obtains /// Requests executor parameters from the runtime effective at given relay-parent. First obtains
@@ -24,7 +24,7 @@ log = { workspace = true, default-features = true }
test-parachain-adder = { path = ".." } test-parachain-adder = { path = ".." }
polkadot-primitives = { path = "../../../../primitives" } polkadot-primitives = { path = "../../../../primitives" }
polkadot-cli = { path = "../../../../cli" } polkadot-cli = { path = "../../../../cli" }
polkadot-service = { path = "../../../../node/service", features = ["rococo-native"] } polkadot-service = { path = "../../../../node/service", features = ["elastic-scaling-experimental", "rococo-native"] }
polkadot-node-primitives = { path = "../../../../node/primitives" } polkadot-node-primitives = { path = "../../../../node/primitives" }
polkadot-node-subsystem = { path = "../../../../node/subsystem" } polkadot-node-subsystem = { path = "../../../../node/subsystem" }
@@ -24,7 +24,7 @@ log = { workspace = true, default-features = true }
test-parachain-undying = { path = ".." } test-parachain-undying = { path = ".." }
polkadot-primitives = { path = "../../../../primitives" } polkadot-primitives = { path = "../../../../primitives" }
polkadot-cli = { path = "../../../../cli" } polkadot-cli = { path = "../../../../cli" }
polkadot-service = { path = "../../../../node/service", features = ["rococo-native"] } polkadot-service = { path = "../../../../node/service", features = ["elastic-scaling-experimental", "rococo-native"] }
polkadot-node-primitives = { path = "../../../../node/primitives" } polkadot-node-primitives = { path = "../../../../node/primitives" }
polkadot-node-subsystem = { path = "../../../../node/subsystem" } polkadot-node-subsystem = { path = "../../../../node/subsystem" }
+30 -4
View File
@@ -27,10 +27,11 @@ use sp_std::{collections::btree_map::BTreeMap, prelude::*};
use polkadot_runtime_parachains::{ use polkadot_runtime_parachains::{
assigner_parachains as parachains_assigner_parachains, assigner_parachains as parachains_assigner_parachains,
configuration as parachains_configuration, disputes as parachains_disputes, configuration as parachains_configuration, disputes as parachains_disputes,
disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, disputes::slashing as parachains_slashing,
inclusion as parachains_inclusion, initializer as parachains_initializer, dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion,
origin as parachains_origin, paras as parachains_paras, initializer as parachains_initializer, origin as parachains_origin, paras as parachains_paras,
paras_inherent as parachains_paras_inherent, runtime_api_impl::v7 as runtime_impl, paras_inherent as parachains_paras_inherent,
runtime_api_impl::{v7 as runtime_impl, vstaging as staging_runtime_impl},
scheduler as parachains_scheduler, session_info as parachains_session_info, scheduler as parachains_scheduler, session_info as parachains_session_info,
shared as parachains_shared, shared as parachains_shared,
}; };
@@ -829,6 +830,7 @@ sp_api::impl_runtime_apis! {
} }
} }
#[api_version(10)]
impl primitives::runtime_api::ParachainHost<Block> for Runtime { impl primitives::runtime_api::ParachainHost<Block> for Runtime {
fn validators() -> Vec<ValidatorId> { fn validators() -> Vec<ValidatorId> {
runtime_impl::validators::<Runtime>() runtime_impl::validators::<Runtime>()
@@ -956,6 +958,30 @@ sp_api::impl_runtime_apis! {
key_ownership_proof, key_ownership_proof,
) )
} }
fn minimum_backing_votes() -> u32 {
runtime_impl::minimum_backing_votes::<Runtime>()
}
fn para_backing_state(para_id: ParaId) -> Option<primitives::async_backing::BackingState> {
runtime_impl::backing_state::<Runtime>(para_id)
}
fn async_backing_params() -> primitives::AsyncBackingParams {
runtime_impl::async_backing_params::<Runtime>()
}
fn approval_voting_params() -> primitives::vstaging::ApprovalVotingParams {
staging_runtime_impl::approval_voting_params::<Runtime>()
}
fn disabled_validators() -> Vec<ValidatorIndex> {
staging_runtime_impl::disabled_validators::<Runtime>()
}
fn node_features() -> primitives::vstaging::NodeFeatures {
staging_runtime_impl::node_features::<Runtime>()
}
} }
impl beefy_primitives::BeefyApi<Block, BeefyId> for Runtime { impl beefy_primitives::BeefyApi<Block, BeefyId> for Runtime {
+14
View File
@@ -0,0 +1,14 @@
title: Enable collators to build on multiple cores
doc:
- audience: Node Dev
description: |
Introduces a `CoreIndex` parameter in `SubmitCollationParams`. This enables
the collators to make use of potentially multiple cores assigned at some relay
chain block. This extra parameter is used by the collator protocol and collation
generation subsystems to forward the collation to the approapriate backing group.
crates:
- name: polkadot-node-collation-generation
- name: polkadot-collator-protocol
bump: minor