diff --git a/Cargo.lock b/Cargo.lock index ae8ee7d..a4ce9d6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6883,6 +6883,7 @@ dependencies = [ "sp-api", "sp-block-builder", "sp-blockchain", + "sp-consensus-slots", "sp-core", "sp-inherents", "sp-keystore", @@ -7213,6 +7214,7 @@ dependencies = [ "sp-inherents", "sp-keystore", "sp-runtime", + "sp-timestamp", "sp-version", "substrate-prometheus-endpoint", "tracing", diff --git a/client/consensus/nimbus-consensus/Cargo.toml b/client/consensus/nimbus-consensus/Cargo.toml index a8ef147..8a0ec12 100644 --- a/client/consensus/nimbus-consensus/Cargo.toml +++ b/client/consensus/nimbus-consensus/Cargo.toml @@ -16,6 +16,7 @@ sp-blockchain = { workspace = true } sp-consensus = { workspace = true } sp-core = { workspace = true } sp-inherents = { workspace = true } +sp-timestamp = { workspace = true } sp-keystore = { workspace = true } sp-runtime = { workspace = true } sp-version = { workspace = true } diff --git a/client/consensus/nimbus-consensus/src/collators.rs b/client/consensus/nimbus-consensus/src/collators.rs index 8a7f7f0..5d3c252 100644 --- a/client/consensus/nimbus-consensus/src/collators.rs +++ b/client/consensus/nimbus-consensus/src/collators.rs @@ -27,12 +27,16 @@ use crate::*; use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface; use cumulus_client_consensus_common::{ParachainBlockImportMarker, ParachainCandidate}; use cumulus_client_consensus_proposer::ProposerInterface; -use cumulus_primitives_core::ParachainBlockData; +use cumulus_primitives_core::{ + relay_chain::{OccupiedCoreAssumption, ValidationCodeHash}, + ParachainBlockData, +}; use cumulus_primitives_parachain_inherent::ParachainInherentData; use futures::prelude::*; use log::{debug, info}; use nimbus_primitives::{CompatibleDigestItem, DigestsProvider, NimbusId, NIMBUS_KEY_ID}; use polkadot_node_primitives::{Collation, MaybeCompressedPoV}; +use polkadot_primitives::Hash as RelayHash; use sc_consensus::{BlockImport, BlockImportParams}; use sp_application_crypto::ByteArray; use sp_consensus::{BlockOrigin, Proposal}; @@ -195,3 +199,54 @@ where ::nimbus_seal(signature) } + +/// Check the `local_validation_code_hash` against the validation code hash in the relay chain +/// state. +/// +/// If the code hashes do not match, it prints a warning. +async fn check_validation_code_or_log( + local_validation_code_hash: &ValidationCodeHash, + para_id: ParaId, + relay_client: &impl RelayChainInterface, + relay_parent: RelayHash, +) { + let state_validation_code_hash = match relay_client + .validation_code_hash(relay_parent, para_id, OccupiedCoreAssumption::Included) + .await + { + Ok(hash) => hash, + Err(error) => { + tracing::debug!( + target: super::LOG_TARGET, + %error, + ?relay_parent, + %para_id, + "Failed to fetch validation code hash", + ); + return; + } + }; + + match state_validation_code_hash { + Some(state) => { + if state != *local_validation_code_hash { + tracing::warn!( + target: super::LOG_TARGET, + %para_id, + ?relay_parent, + ?local_validation_code_hash, + relay_validation_code_hash = ?state, + "Parachain code doesn't match validation code stored in the relay chain state.", + ); + } + } + None => { + tracing::warn!( + target: super::LOG_TARGET, + %para_id, + ?relay_parent, + "Could not find validation code for parachain in the relay chain state.", + ); + } + } +} diff --git a/client/consensus/nimbus-consensus/src/collators/basic.rs b/client/consensus/nimbus-consensus/src/collators/basic.rs index 68f04c7..58e1a4b 100644 --- a/client/consensus/nimbus-consensus/src/collators/basic.rs +++ b/client/consensus/nimbus-consensus/src/collators/basic.rs @@ -16,10 +16,10 @@ use crate::*; use cumulus_client_collator::service::ServiceInterface as CollatorServiceInterface; -use cumulus_client_consensus_common::ParachainBlockImportMarker; +use cumulus_client_consensus_common::{self as consensus_common, ParachainBlockImportMarker}; use cumulus_client_consensus_proposer::ProposerInterface; use cumulus_primitives_core::{ - relay_chain::{BlockId as RBlockId, Hash as PHash}, + relay_chain::{BlockId as RBlockId, Hash as PHash, ValidationCode}, CollectCollationInfo, ParaId, PersistedValidationData, }; use cumulus_relay_chain_interface::{OverseerHandle, RelayChainInterface}; @@ -27,9 +27,10 @@ use futures::prelude::*; use nimbus_primitives::{DigestsProvider, NimbusApi, NimbusId}; use polkadot_node_primitives::CollationResult; use polkadot_primitives::CollatorPair; -use sc_client_api::{BlockBackend, BlockOf}; -use sp_api::ProvideRuntimeApi; +use sc_client_api::{AuxStore, BlockBackend, BlockOf, StateBackend}; +use sp_api::{CallApiAt, ProvideRuntimeApi}; use sp_blockchain::HeaderBackend; +use sp_consensus_slots::{Slot, SlotDuration}; use sp_core::Decode; use sp_inherents::CreateInherentDataProviders; use sp_keystore::KeystorePtr; @@ -44,6 +45,11 @@ pub struct Params { pub para_id: ParaId, /// A handle to the relay-chain client's "Overseer" or task orchestrator. pub overseer_handle: OverseerHandle, + /// The length of slots in the relay chain. + pub relay_chain_slot_duration: Duration, + /// The length of slots in this parachain. + /// If the parachain doesn't have slot and rely only on relay slots, set it to None. + pub slot_duration: Option, /// The underlying block proposer this should call into. pub proposer: Proposer, /// The block import handle. @@ -79,8 +85,10 @@ pub async fn run( BI: BlockImport + ParachainBlockImportMarker + Send + Sync + 'static, Client: ProvideRuntimeApi + BlockOf + + AuxStore + HeaderBackend + BlockBackend + + CallApiAt + Send + Sync + 'static, @@ -112,6 +120,9 @@ pub async fn run( .. } = params; + let mut last_processed_slot = 0; + let mut last_relay_chain_block = Default::default(); + while let Some(request) = collation_requests.next().await { macro_rules! reject_with_error { ($err:expr) => {{ @@ -142,6 +153,25 @@ pub async fn run( continue; } + let Ok(Some(code)) = para_client + .state_at(parent_hash) + .map_err(drop) + .and_then(|s| { + s.storage(&sp_core::storage::well_known_keys::CODE) + .map_err(drop) + }) + else { + continue; + }; + + super::check_validation_code_or_log( + &ValidationCode::from(code).hash(), + para_id, + &relay_client, + *request.relay_parent(), + ) + .await; + let relay_parent_header = match relay_client .header(RBlockId::hash(*request.relay_parent())) .await @@ -165,6 +195,54 @@ pub async fn run( Err(e) => reject_with_error!(e), }; + // Determine which is the current slot + let (slot_now, timestamp) = match consensus_common::relay_slot_and_timestamp( + &relay_parent_header, + params.relay_chain_slot_duration, + ) { + None => { + tracing::trace!( + target: crate::LOG_TARGET, + relay_parent = ?relay_parent_header, + relay_chain_slot_duration = ?params.relay_chain_slot_duration, + "Fail to get the relay slot for this relay block!" + ); + continue; + } + Some((relay_slot, relay_timestamp)) => { + let our_slot = if let Some(slot_duration) = params.slot_duration { + Slot::from_timestamp(relay_timestamp, slot_duration) + } else { + // If there is no slot duration, we assume that the parachain use the relay slot directly + relay_slot + }; + tracing::debug!( + target: crate::LOG_TARGET, + ?relay_slot, + para_slot = ?our_slot, + ?relay_timestamp, + slot_duration = ?params.slot_duration, + relay_chain_slot_duration = ?params.relay_chain_slot_duration, + "Adjusted relay-chain slot to parachain slot" + ); + (our_slot, relay_timestamp) + } + }; + + // With async backing this function will be called every relay chain block. + // + // Most parachains currently run with 12 seconds slots and thus, they would try to + // produce multiple blocks per slot which very likely would fail on chain. Thus, we have + // this "hack" to only produce one block per slot per relay chain fork. + // + // With https://github.com/paritytech/polkadot-sdk/issues/3168 this implementation will be + // obsolete and also the underlying issue will be fixed. + if last_processed_slot >= *slot_now + && last_relay_chain_block < *relay_parent_header.number() + { + continue; + } + let inherent_data = try_request!( create_inherent_data( &create_inherent_data_providers, @@ -174,6 +252,7 @@ pub async fn run( &relay_client, *request.relay_parent(), nimbus_id.clone(), + Some(timestamp) ) .await ); @@ -216,5 +295,8 @@ pub async fn run( request.complete(None); tracing::debug!(target: crate::LOG_TARGET, "No block proposal"); } + + last_processed_slot = *slot_now; + last_relay_chain_block = *relay_parent_header.number(); } } diff --git a/client/consensus/nimbus-consensus/src/collators/lookahead.rs b/client/consensus/nimbus-consensus/src/collators/lookahead.rs index 25be997..02bcaed 100644 --- a/client/consensus/nimbus-consensus/src/collators/lookahead.rs +++ b/client/consensus/nimbus-consensus/src/collators/lookahead.rs @@ -207,7 +207,7 @@ where }; // Determine which is the current slot - let slot_now = match consensus_common::relay_slot_and_timestamp( + let (slot_now, timestamp) = match consensus_common::relay_slot_and_timestamp( &relay_parent_header, params.relay_chain_slot_duration, ) { @@ -236,7 +236,7 @@ where relay_chain_slot_duration = ?params.relay_chain_slot_duration, "Adjusted relay-chain slot to parachain slot" ); - our_slot + (our_slot, relay_timestamp) } }; @@ -343,6 +343,7 @@ where ¶ms.relay_client, relay_parent, author_id.clone(), + Some(timestamp), ) .await { @@ -355,19 +356,21 @@ where // Compute the hash of the parachain runtime bytecode that we using to build the block. // The hash will be send to relay validators alongside the candidate. - let validation_code_hash = match params.code_hash_provider.code_hash_at(parent_hash) - { - None => { - tracing::error!( - target: crate::LOG_TARGET, - ?parent_hash, - "Could not fetch validation code hash" - ); - break; - } - Some(validation_code_hash) => validation_code_hash, + let Some(validation_code_hash) = + params.code_hash_provider.code_hash_at(parent_hash) + else { + tracing::error!(target: crate::LOG_TARGET, ?parent_hash, "Could not fetch validation code hash"); + break; }; + super::check_validation_code_or_log( + &validation_code_hash, + params.para_id, + ¶ms.relay_client, + relay_parent, + ) + .await; + let allowed_pov_size = { // Cap the percentage at 85% (see https://github.com/paritytech/polkadot-sdk/issues/6020) let capped_percentage = params.max_pov_percentage.min(85); diff --git a/client/consensus/nimbus-consensus/src/lib.rs b/client/consensus/nimbus-consensus/src/lib.rs index 6df24ae..d4eef79 100644 --- a/client/consensus/nimbus-consensus/src/lib.rs +++ b/client/consensus/nimbus-consensus/src/lib.rs @@ -98,7 +98,8 @@ where } } -/// Explicitly creates the inherent data for parachain block authoring. +/// Explicitly creates the inherent data for parachain block authoring and overrides +/// the timestamp inherent data with the one provided, if any. pub(crate) async fn create_inherent_data( create_inherent_data_providers: &CIDP, para_id: ParaId, @@ -107,6 +108,7 @@ pub(crate) async fn create_inherent_data( relay_client: &RClient, relay_parent: PHash, author_id: NimbusId, + timestamp: impl Into>, ) -> Result<(ParachainInherentData, InherentData), Box> where Block: BlockT, @@ -131,7 +133,7 @@ where } }; - let other_inherent_data = create_inherent_data_providers + let mut other_inherent_data = create_inherent_data_providers .create_inherent_data_providers(parent, (relay_parent, validation_data.clone(), author_id)) .map_err(|e| e as Box) .await? @@ -139,6 +141,10 @@ where .await .map_err(Box::new)?; + if let Some(timestamp) = timestamp.into() { + other_inherent_data.replace_data(sp_timestamp::INHERENT_IDENTIFIER, ×tamp); + } + Ok((paras_inherent_data, other_inherent_data)) } diff --git a/pallets/async-backing/src/consensus_hook.rs b/pallets/async-backing/src/consensus_hook.rs index d7b4835..fbfd34e 100644 --- a/pallets/async-backing/src/consensus_hook.rs +++ b/pallets/async-backing/src/consensus_hook.rs @@ -29,17 +29,50 @@ use frame_support::pallet_prelude::*; use sp_consensus_slots::Slot; use sp_std::{marker::PhantomData, num::NonZeroU32}; -/// A consensus hook for a fixed block processing velocity and unincluded segment capacity. +/// A consensus hook that enforces fixed block production velocity and unincluded segment capacity. /// -/// Relay chain slot duration must be provided in milliseconds. -pub struct FixedVelocityConsensusHook(PhantomData); - -impl ConsensusHook - for FixedVelocityConsensusHook +/// It keeps track of relay chain slot information and parachain blocks authored per relay chain +/// slot. +/// +/// # Type Parameters +/// - `T` - The runtime configuration trait +/// - `RELAY_CHAIN_SLOT_DURATION_MILLIS` - Duration of relay chain slots in milliseconds +/// - `V` - Maximum number of blocks that can be authored per relay chain parent (velocity) +/// - `C` - Maximum capacity of unincluded segment +/// +/// # Example Configuration +/// ```ignore +/// type ConsensusHook = FixedVelocityConsensusHook; +/// ``` +/// This configures: +/// - 6 second relay chain slots +/// - Maximum 2 blocks per slot +/// - Maximum 8 blocks in unincluded segment +pub struct FixedVelocityConsensusHook< + T, + const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32, + const V: u32, + const C: u32, +>(PhantomData); + +impl< + T: pallet::Config, + const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32, + const V: u32, + const C: u32, + > ConsensusHook for FixedVelocityConsensusHook where ::Moment: Into, { - // Validates the number of authored blocks within the slot with respect to the `V + 1` limit. + /// Consensus hook that performs validations on the provided relay chain state + /// proof: + /// - Ensures blocks are not produced faster than the specified velocity `V` + /// - Verifies parachain slot alignment with relay chain slot + /// + /// # Panics + /// - When the relay chain slot from the state is smaller than the slot from the proof + /// - When the number of authored blocks exceeds velocity limit + /// - When parachain slot is ahead of the calculated slot from relay chain fn on_state_proof(state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) { let relay_chain_slot = state_proof .read_slot() @@ -49,7 +82,12 @@ where } } -impl FixedVelocityConsensusHook +impl< + T: pallet::Config, + const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32, + const V: u32, + const C: u32, + > FixedVelocityConsensusHook where ::Moment: Into, { @@ -59,32 +97,50 @@ where // Ensure velocity is non-zero. let velocity = V.max(1); - // Get and verify the parachain slot - let new_slot = T::GetAndVerifySlot::get_and_verify_slot(&relay_chain_slot) - .expect("slot number mismatch"); - - // Update Slot Info - let authored = match SlotInfo::::get() { - Some((slot, authored)) if slot == new_slot => { + let (relay_chain_slot, authored_in_relay) = match pallet::SlotInfo::::get() { + Some((slot, authored)) if slot == relay_chain_slot => { if !T::AllowMultipleBlocksPerSlot::get() { panic!("Block invalid; Supplied slot number is not high enough"); } - authored + 1 + + (slot, authored) } - Some((slot, _)) if slot < new_slot => 1, - Some(..) => { - panic!("slot moved backwards") + Some((slot, _)) if slot < relay_chain_slot => (relay_chain_slot, 0), + Some((slot, _)) => { + panic!("Slot moved backwards: stored_slot={slot:?}, relay_chain_slot={relay_chain_slot:?}") } - None => 1, + None => (relay_chain_slot, 0), }; - // Perform checks. - if authored > velocity + 1 { - panic!("authored blocks limit is reached for the slot") + // We need to allow one additional block to be built to fill the unincluded segment. + if authored_in_relay > velocity { + panic!("authored blocks limit is reached for the slot: relay_chain_slot={relay_chain_slot:?}, authored={authored_in_relay:?}, velocity={velocity:?}"); } // Store new slot info - SlotInfo::::put((new_slot, authored)); + SlotInfo::::put((relay_chain_slot, authored_in_relay + 1)); + + // Get and verify the parachain slot + let para_slot = T::GetAndVerifySlot::get_and_verify_slot(&relay_chain_slot) + .expect("slot number mismatch"); + + // Convert relay chain timestamp. + let relay_chain_timestamp = + u64::from(RELAY_CHAIN_SLOT_DURATION_MILLIS).saturating_mul(*relay_chain_slot); + + let para_slot_duration = SlotDuration::from_millis(T::SlotDuration::get().into()); + let para_slot_from_relay = + Slot::from_timestamp(relay_chain_timestamp.into(), para_slot_duration); + + if *para_slot > *para_slot_from_relay { + panic!( + "Parachain slot is too far in the future: parachain_slot={:?}, derived_from_relay_slot={:?} velocity={:?}, relay_chain_slot={:?}", + para_slot, + para_slot_from_relay, + velocity, + relay_chain_slot + ); + } // Account weights let weight = T::DbWeight::get().reads_writes(1, 1); @@ -92,15 +148,19 @@ where // Return weight and unincluded segment capacity ( weight, - NonZeroU32::new(sp_std::cmp::max(C, 1)) + NonZeroU32::new(core::cmp::max(C, 1)) .expect("1 is the minimum value and non-zero; qed") .into(), ) } } -impl - FixedVelocityConsensusHook +impl< + T: pallet::Config + parachain_system::Config, + const RELAY_CHAIN_SLOT_DURATION_MILLIS: u32, + const V: u32, + const C: u32, + > FixedVelocityConsensusHook { /// Whether it is legal to extend the chain, assuming the given block is the most /// recently included one as-of the relay parent that will be built against, and @@ -114,7 +174,7 @@ impl /// is more recent than the included block itself. pub fn can_build_upon(included_hash: T::Hash, new_slot: Slot) -> bool { let velocity = V.max(1); - let (last_slot, authored_so_far) = match pallet::Pallet::::slot_info() { + let (last_slot, authored_so_far) = match pallet::SlotInfo::::get() { None => return true, Some(x) => x, }; @@ -127,6 +187,8 @@ impl return false; } + // Check that we have not authored more than `V + 1` parachain blocks in the current relay + // chain slot. if last_slot == new_slot { authored_so_far < velocity + 1 } else { diff --git a/pallets/async-backing/src/lib.rs b/pallets/async-backing/src/lib.rs index a5d77c1..2c52c51 100644 --- a/pallets/async-backing/src/lib.rs +++ b/pallets/async-backing/src/lib.rs @@ -27,6 +27,7 @@ pub use pallet::*; use frame_support::pallet_prelude::*; use sp_consensus_slots::{Slot, SlotDuration}; +use sp_runtime::SaturatedConversion; /// The InherentIdentifier for nimbus's extension inherent pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"nimb-ext"; @@ -95,16 +96,48 @@ pub mod pallet { /// A way to get the current parachain slot and verify it's validity against the relay slot. type GetAndVerifySlot: GetAndVerifySlot; + /// The slot duration Nimbus should run with, expressed in milliseconds. + /// The effective value of this type should not change while the chain is running. + #[pallet::constant] + type SlotDuration: Get; + + /// TODO: Remove this constant once chopsticks has been updated + /// - https://github.com/AcalaNetwork/chopsticks/blob/1a84b55097d2efdfaee64964b4b36af7c741d854/packages/core/src/utils/index.ts#L132 + /// /// Purely informative, but used by mocking tools like chospticks to allow knowing how to mock /// blocks #[pallet::constant] type ExpectedBlockTime: Get; } - /// First tuple element is the highest slot that has been seen in the history of this chain. - /// Second tuple element is the number of authored blocks so far. - /// This is a strictly-increasing value if T::AllowMultipleBlocksPerSlot = false. + /// Current relay chain slot paired with a number of authored blocks. + /// + /// This is updated in [`FixedVelocityConsensusHook::on_state_proof`] with the current relay + /// chain slot as provided by the relay chain state proof. #[pallet::storage] #[pallet::getter(fn slot_info)] pub type SlotInfo = StorageValue<_, (Slot, u32), OptionQuery>; } + +impl frame_support::traits::PostInherents for Pallet { + fn post_inherents() { + let slot_duration = T::SlotDuration::get(); + assert!( + !slot_duration.is_zero(), + "Nimbus slot duration cannot be zero." + ); + // Get the parachain slot + let now = pallet_timestamp::Now::::get(); + let timestamp_slot = now / slot_duration; + let timestamp_slot = Slot::from(timestamp_slot.saturated_into::()); + + // Get the relay chain slot + let (current_slot, _) = SlotInfo::::get().expect("Relay slot to exist"); + + assert_eq!( + current_slot, timestamp_slot, + "The parachain timestamp slot must match the relay chain slot. This likely means that the configured block \ + time in the node and/or rest of the runtime is not compatible with Nimbus's `SlotDuration`", + ); + } +} diff --git a/pallets/async-backing/src/mock.rs b/pallets/async-backing/src/mock.rs index 1b3d4eb..121ab27 100644 --- a/pallets/async-backing/src/mock.rs +++ b/pallets/async-backing/src/mock.rs @@ -85,12 +85,13 @@ impl pallet_timestamp::Config for Test { parameter_types! { pub const AllowMultipleBlocksPerSlot: bool = true; - pub const SlotDuration: u64 = 12000; + pub const SlotDuration: u64 = 6_000; } impl async_backing::Config for Test { type AllowMultipleBlocksPerSlot = AllowMultipleBlocksPerSlot; type GetAndVerifySlot = RelaySlot; + type SlotDuration = SlotDuration; type ExpectedBlockTime = ConstU64<1>; } diff --git a/pallets/async-backing/src/tests.rs b/pallets/async-backing/src/tests.rs index db7cba4..7c26f6e 100644 --- a/pallets/async-backing/src/tests.rs +++ b/pallets/async-backing/src/tests.rs @@ -18,7 +18,7 @@ use crate::consensus_hook::FixedVelocityConsensusHook; use crate::mock::*; use std::ops::Deref; -type ConsensusHook = FixedVelocityConsensusHook; +type ConsensusHook = FixedVelocityConsensusHook; fn assert_slot_info_eq(slot_number: u64, authored: u32) { assert_eq!(AsyncBacking::slot_info().unwrap().0.deref(), &slot_number); diff --git a/template/node/Cargo.toml b/template/node/Cargo.toml index 1a441cf..ed94d75 100644 --- a/template/node/Cargo.toml +++ b/template/node/Cargo.toml @@ -75,6 +75,7 @@ sp-runtime = { workspace = true } sp-session = { workspace = true } sp-timestamp = { workspace = true } sp-transaction-pool = { workspace = true } +sp-consensus-slots = { workspace = true } # Cumulus dependencies cumulus-client-cli = { workspace = true } diff --git a/template/node/src/service.rs b/template/node/src/service.rs index ba743b4..3fbaf19 100644 --- a/template/node/src/service.rs +++ b/template/node/src/service.rs @@ -47,6 +47,7 @@ use sc_network::{ use sc_service::{Configuration, PartialComponents, TFullBackend, TFullClient, TaskManager}; use sc_telemetry::{Telemetry, TelemetryHandle, TelemetryWorker, TelemetryWorkerHandle}; use sp_api::ProvideRuntimeApi; +use sp_consensus_slots::SlotDuration; use sp_keystore::KeystorePtr; use sp_runtime::traits::Block as BlockT; use substrate_prometheus_endpoint::Registry; @@ -411,6 +412,8 @@ fn start_consensus( additional_digests_provider: (), collator_key, authoring_duration: Duration::from_millis(500), + relay_chain_slot_duration: Duration::from_millis(6_000), + slot_duration: Some(SlotDuration::from_millis(6_000)), }; let fut = nimbus_consensus::collators::basic::run::( diff --git a/template/runtime/src/lib.rs b/template/runtime/src/lib.rs index 1e76093..4987453 100644 --- a/template/runtime/src/lib.rs +++ b/template/runtime/src/lib.rs @@ -195,8 +195,6 @@ pub mod opaque { impl_opaque_keys! { pub struct SessionKeys { - //TODO this was called author_inherent in the old runtime. - // Can I just rename it like this? pub nimbus: AuthorInherent, } } @@ -219,7 +217,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { /// up by `pallet_aura` to implement `fn slot_duration()`. /// /// Change this to adjust the block time. -pub const MILLISECS_PER_BLOCK: u64 = 12000; +pub const MILLISECS_PER_BLOCK: u64 = 6000; // NOTE: Currently it is not possible to change the slot duration after the chain has started. // Attempting to do so will brick block production. @@ -345,7 +343,10 @@ impl frame_system::Config for Runtime { type SingleBlockMigrations = (); type MultiBlockMigrator = (); type PreInherents = (); - type PostInherents = (); + type PostInherents = ( + // Validate timestamp provided by the consensus client + NimbusAsyncBacking, + ); type PostTransactions = (); type ExtensionsWeightInfo = (); } @@ -432,6 +433,7 @@ pub const BLOCK_PROCESSING_VELOCITY: u32 = 1; type ConsensusHook = pallet_async_backing::consensus_hook::FixedVelocityConsensusHook< Runtime, + RELAY_CHAIN_SLOT_DURATION_MILLIS, BLOCK_PROCESSING_VELOCITY, UNINCLUDED_SEGMENT_CAPACITY, >; @@ -667,13 +669,14 @@ impl pallet_author_slot_filter::Config for Runtime { } parameter_types! { - pub const ExpectedBlockTime: u64 = MILLISECS_PER_BLOCK; + pub const SlotDuration: u64 = MILLISECS_PER_BLOCK; } impl pallet_async_backing::Config for Runtime { type AllowMultipleBlocksPerSlot = ConstBool; type GetAndVerifySlot = pallet_async_backing::RelaySlot; - type ExpectedBlockTime = ExpectedBlockTime; + type SlotDuration = SlotDuration; + type ExpectedBlockTime = SlotDuration; } parameter_types! {