diff --git a/bin/node-sassafras/node/src/service.rs b/bin/node-sassafras/node/src/service.rs index 514d2ff71bd1a..a8cd614882ea7 100644 --- a/bin/node-sassafras/node/src/service.rs +++ b/bin/node-sassafras/node/src/service.rs @@ -264,7 +264,7 @@ pub fn new_full(mut config: Configuration) -> Result let slot_duration = sassafras_link.genesis_config().slot_duration(); - let sassafras_config = sc_consensus_sassafras::SassafrasParams { + let sassafras_params = sc_consensus_sassafras::SassafrasParams { client: client.clone(), keystore: keystore_container.sync_keystore(), select_chain, @@ -274,7 +274,7 @@ pub fn new_full(mut config: Configuration) -> Result sync_oracle: network.clone(), justification_sync_link: network.clone(), force_authoring, - create_inherent_data_providers: move |_, ()| async move { + create_inherent_data_providers: move |_, _| async move { let timestamp = sp_timestamp::InherentDataProvider::from_system_time(); let slot = @@ -286,7 +286,7 @@ pub fn new_full(mut config: Configuration) -> Result }, }; - let sassafras = sc_consensus_sassafras::start_sassafras(sassafras_config)?; + let sassafras = sc_consensus_sassafras::start_sassafras(sassafras_params)?; // the Sassafras authoring task is considered essential, i.e. if it // fails we take down the service with it. diff --git a/bin/node-sassafras/runtime/src/lib.rs b/bin/node-sassafras/runtime/src/lib.rs index 7697be0a3f396..e78b280db12da 100644 --- a/bin/node-sassafras/runtime/src/lib.rs +++ b/bin/node-sassafras/runtime/src/lib.rs @@ -41,21 +41,55 @@ pub type BlockNumber = u32; /// Alias to 512-bit hash when used in the context of a transaction signature on the chain. pub type Signature = MultiSignature; +/// Index of a transaction in the chain. +pub type Index = u32; + +/// A hash of some data used by the chain. +pub type Hash = sp_core::H256; + +/// Block header type as expected by this runtime. +pub type Header = generic::Header; + +/// The SignedExtension to the basic transaction logic. +pub type SignedExtra = ( + frame_system::CheckNonZeroSender, + frame_system::CheckSpecVersion, + frame_system::CheckTxVersion, + frame_system::CheckGenesis, + frame_system::CheckEra, + frame_system::CheckNonce, + frame_system::CheckWeight, + pallet_transaction_payment::ChargeTransactionPayment, +); + +/// Unchecked extrinsic type as expected by this runtime. +pub type UncheckedExtrinsic = + generic::UncheckedExtrinsic; + +/// Block type as expected by this runtime. +pub type Block = generic::Block; + /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <::Signer as IdentifyAccount>::AccountId; +/// The address format for describing accounts. +pub type Address = sp_runtime::MultiAddress; + /// Balance of an account. pub type Balance = u128; -/// Index of a transaction in the chain. -pub type Index = u32; - -/// A hash of some data used by the chain. -pub type Hash = sp_core::H256; +/// The payload being signed in transactions. +pub type SignedPayload = generic::SignedPayload; -/// Type used for expressing timestamp. -pub type Moment = u64; +/// Executive: handles dispatch to the various modules. +pub type Executive = frame_executive::Executive< + Runtime, + Block, + frame_system::ChainContext, + Runtime, + AllPalletsWithSystem, +>; /// Opaque types. These are used by the CLI to instantiate machinery that don't need to know /// the specifics of the runtime. They can then be made to be agnostic over specific formats @@ -65,7 +99,6 @@ pub mod opaque { use super::*; pub use sp_runtime::OpaqueExtrinsic as UncheckedExtrinsic; - /// Opaque block header type. pub type Header = generic::Header; /// Opaque block type. @@ -81,18 +114,11 @@ impl_opaque_keys! { } } -// To learn more about runtime versioning and what each of the following value means: -// https://docs.substrate.io/v3/runtime/upgrades#runtime-versioning #[sp_version::runtime_version] pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("node-sassafras"), impl_name: create_runtime_str!("node-sassafras"), authoring_version: 1, - // The version of the runtime specification. A full node will not attempt to use its native - // runtime in substitute for the on-chain Wasm runtime unless all of `spec_name`, - // `spec_version`, and `authoring_version` are the same between Wasm and native. - // This value is set to 100 to notify Polkadot-JS App (https://polkadot.js.org/apps) to use - // the compatible custom types. spec_version: 100, impl_version: 1, apis: RUNTIME_API_VERSIONS, @@ -100,32 +126,13 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { state_version: 1, }; -/// This determines the average expected block time that we are targeting. -/// Blocks will be produced at a minimum duration defined by `SLOT_DURATION`. -/// `SLOT_DURATION` is picked up by `pallet_timestamp` which is in turn picked -/// up by `pallet_sassafras` to implement `fn slot_duration()`. -/// -/// Change this to adjust the block time. -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. -pub const SLOT_DURATION: u64 = MILLISECS_PER_BLOCK; +/// Sassafras slot duration in milliseconds +pub const SLOT_DURATION_IN_MILLISECONDS: u64 = 3000; -// TODO-SASS-P4: this is an intentional small value used for testing -pub const EPOCH_DURATION_IN_BLOCKS: BlockNumber = 10; - -pub const EPOCH_DURATION_IN_SLOTS: u64 = { - const SLOT_FILL_RATE: f64 = MILLISECS_PER_BLOCK as f64 / SLOT_DURATION as f64; - - (EPOCH_DURATION_IN_BLOCKS as f64 * SLOT_FILL_RATE) as u64 -}; - -// Time is measured by number of blocks. -pub const MINUTES: BlockNumber = 60_000 / (MILLISECS_PER_BLOCK as BlockNumber); -pub const HOURS: BlockNumber = MINUTES * 60; -pub const DAYS: BlockNumber = HOURS * 24; +/// Sassafras epoch duration in slots. +pub const EPOCH_DURATION_IN_SLOTS: u64 = 10; +/// Max authorities for both Sassafras and Grandpa. pub const MAX_AUTHORITIES: u32 = 32; /// The version information used to identify this runtime when compiled natively. @@ -134,8 +141,8 @@ pub fn native_version() -> NativeVersion { NativeVersion { runtime_version: VERSION, can_author_with: Default::default() } } -const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); - +// Required to send unsigned transactoins from Sassafras pallet +// TODO-SASS-P2 double check (isn't grandpa requiring the same thing??? impl frame_system::offchain::SendTransactionTypes for Runtime where RuntimeCall: From, @@ -144,6 +151,8 @@ where type OverarchingCall = RuntimeCall; } +const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75); + parameter_types! { pub const BlockHashCount: BlockNumber = 2400; pub const Version: RuntimeVersion = VERSION; @@ -184,14 +193,9 @@ impl frame_system::Config for Runtime { type MaxConsumers = frame_support::traits::ConstU32<16>; } -parameter_types! { - pub const EpochDuration: u64 = EPOCH_DURATION_IN_SLOTS; - pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK; -} - impl pallet_sassafras::Config for Runtime { - type EpochDuration = EpochDuration; - type ExpectedBlockTime = ExpectedBlockTime; + type SlotDuration = ConstU64; + type EpochDuration = ConstU64; #[cfg(feature = "use-session-pallet")] type EpochChangeTrigger = pallet_sassafras::ExternalTrigger; #[cfg(not(feature = "use-session-pallet"))] @@ -217,7 +221,7 @@ impl pallet_grandpa::Config for Runtime { impl pallet_timestamp::Config for Runtime { type Moment = u64; type OnTimestampSet = (); - type MinimumPeriod = ConstU64<{ SLOT_DURATION / 2 }>; + type MinimumPeriod = ConstU64<{ SLOT_DURATION_IN_MILLISECONDS / 2 }>; type WeightInfo = (); } @@ -297,43 +301,6 @@ construct_runtime!( } ); -/// The address format for describing accounts. -pub type Address = sp_runtime::MultiAddress; - -/// Block header type as expected by this runtime. -pub type Header = generic::Header; - -/// Block type as expected by this runtime. -pub type Block = generic::Block; - -/// The SignedExtension to the basic transaction logic. -pub type SignedExtra = ( - frame_system::CheckNonZeroSender, - frame_system::CheckSpecVersion, - frame_system::CheckTxVersion, - frame_system::CheckGenesis, - frame_system::CheckEra, - frame_system::CheckNonce, - frame_system::CheckWeight, - pallet_transaction_payment::ChargeTransactionPayment, -); - -/// Unchecked extrinsic type as expected by this runtime. -pub type UncheckedExtrinsic = - generic::UncheckedExtrinsic; - -/// The payload being signed in transactions. -pub type SignedPayload = generic::SignedPayload; - -/// Executive: handles dispatch to the various modules. -pub type Executive = frame_executive::Executive< - Runtime, - Block, - frame_system::ChainContext, - Runtime, - AllPalletsWithSystem, ->; - #[cfg(feature = "runtime-benchmarks")] #[macro_use] extern crate frame_benchmarking; @@ -411,8 +378,8 @@ impl_runtime_apis! { impl sp_consensus_sassafras::SassafrasApi for Runtime { fn configuration() -> sp_consensus_sassafras::SassafrasConfiguration { sp_consensus_sassafras::SassafrasConfiguration { - slot_duration: Sassafras::slot_duration(), - epoch_duration: EpochDuration::get(), + slot_duration: SLOT_DURATION_IN_MILLISECONDS, + epoch_duration: EPOCH_DURATION_IN_SLOTS, authorities: Sassafras::authorities().to_vec(), randomness: Sassafras::randomness(), threshold_params: Sassafras::config(), @@ -435,9 +402,7 @@ impl_runtime_apis! { SessionKeys::generate(seed) } - fn decode_session_keys( - encoded: Vec, - ) -> Option, KeyTypeId)>> { + fn decode_session_keys(encoded: Vec) -> Option, KeyTypeId)>> { SessionKeys::decode_into_raw_public_keys(&encoded) } } diff --git a/frame/sassafras/src/lib.rs b/frame/sassafras/src/lib.rs index c575ef8f33233..d77e34344f835 100644 --- a/frame/sassafras/src/lib.rs +++ b/frame/sassafras/src/lib.rs @@ -98,21 +98,14 @@ pub mod pallet { /// Configuration parameters. #[pallet::config] - #[pallet::disable_frame_system_supertrait_check] - pub trait Config: pallet_timestamp::Config + SendTransactionTypes> { - /// The amount of time, in slots, that each epoch should last. - /// NOTE: Currently it is not possible to change the epoch duration after the chain has - /// started. Attempting to do so will brick block production. + pub trait Config: frame_system::Config + SendTransactionTypes> { + /// The amount of time, in milliseconds, that each slot should last. #[pallet::constant] - type EpochDuration: Get; + type SlotDuration: Get; - /// The expected average block time at which Sassafras should be creating - /// blocks. Since Sassafras is probabilistic it is not trivial to figure out - /// what the expected average block time should be based on the slot - /// duration and the security parameter `c` (where `1 - c` represents - /// the probability of a slot being empty). + /// The amount of time, in slots, that each epoch should last. #[pallet::constant] - type ExpectedBlockTime: Get; + type EpochDuration: Get; /// Sassafras requires some logic to be triggered on every block to query for whether an /// epoch has ended and to perform the transition to the next epoch. @@ -130,13 +123,11 @@ pub mod pallet { type MaxTickets: Get; } - // TODO-SASS-P2 /// Sassafras runtime errors. #[pallet::error] pub enum Error { /// Submitted configuration is invalid. InvalidConfiguration, - // TODO-SASS P2 ... } /// Current epoch index. @@ -302,6 +293,7 @@ pub mod pallet { #[pallet::call] impl Pallet { /// Submit next epoch tickets. + /// /// TODO-SASS-P3: this is an unsigned extrinsic. Can we remov ethe weight? #[pallet::weight(10_000)] pub fn submit_tickets( @@ -321,16 +313,22 @@ pub mod pallet { Ok(()) } - /// Plan an epoch config change. The epoch config change is recorded and will be enacted on - /// the next call to `enact_session_change`. The config will be activated one epoch after. - /// Multiple calls to this method will replace any existing planned config change that had - /// not been enacted yet. + /// Plan an epoch config change. + /// + /// The epoch config change is recorded and will be enacted on the next call to + /// `enact_session_change`. + /// + /// The config will be activated one epoch after. Multiple calls to this method will + /// replace any existing planned config change that had not been enacted yet. + /// + /// TODO: TODO-SASS-P4: proper weight #[pallet::weight(10_000)] pub fn plan_config_change( origin: OriginFor, config: SassafrasEpochConfiguration, ) -> DispatchResult { ensure_root(origin)?; + ensure!( config.redundancy_factor != 0 && config.attempts_number != 0, Error::::InvalidConfiguration @@ -425,13 +423,13 @@ pub mod pallet { // Inherent methods impl Pallet { - /// Determine the Sassafras slot duration based on the Timestamp module configuration. - pub fn slot_duration() -> T::Moment { - // TODO-SASS-P2: clarify why this is doubled (copied verbatim from BABE) - // We double the minimum block-period so each author can always propose within - // the majority of their slot. - ::MinimumPeriod::get().saturating_mul(2u32.into()) - } + // // TODO-SASS-P2: I don't think this is really required + // /// Determine the Sassafras slot duration based on the Timestamp module configuration. + // pub fn slot_duration() -> T::Moment { + // // We double the minimum block-period so each author can always propose within + // // the majority of their slot. + // ::MinimumPeriod::get().saturating_mul(2u32.into()) + // } /// Determine whether an epoch change should take place at this block. /// Assumes that initialization has already taken place. @@ -728,17 +726,26 @@ impl Pallet { metadata.segments_count = segments_count; } - /// Submit next epoch validator tickets via an unsigned extrinsic. + /// Submit next epoch validator tickets via an unsigned extrinsic constructed with a call to + /// `submit_unsigned_transaction`. + /// /// The submitted tickets are added to the `NextTickets` list as long as the extrinsic has /// is called within the first half of the epoch. That is, tickets received within the /// second half are dropped. + /// /// TODO-SASS-P3: we have to add the zk validity proofs pub fn submit_tickets_unsigned_extrinsic(mut tickets: Vec) -> bool { log::debug!(target: "sassafras", "🌳 @@@@@@@@@@ submitting {} tickets", tickets.len()); tickets.sort_unstable(); let tickets = BoundedVec::truncate_from(tickets); let call = Call::submit_tickets { tickets }; - SubmitTransaction::>::submit_unsigned_transaction(call.into()).is_ok() + match SubmitTransaction::>::submit_unsigned_transaction(call.into()) { + Ok(_) => true, + Err(e) => { + log::error!(target: "runtime::sassafras", "Error submitting tickets {:?}", e); + false + }, + } } } diff --git a/frame/sassafras/src/mock.rs b/frame/sassafras/src/mock.rs index df36f60ede33d..a8c9ca6e856d7 100644 --- a/frame/sassafras/src/mock.rs +++ b/frame/sassafras/src/mock.rs @@ -35,6 +35,7 @@ use sp_runtime::{ traits::IdentityLookup, }; +const SLOT_DURATION: u64 = 1000; const EPOCH_DURATION: u64 = 10; const MAX_TICKETS: u32 = 6; @@ -88,8 +89,8 @@ where } impl pallet_sassafras::Config for Test { + type SlotDuration = ConstU64; type EpochDuration = ConstU64; - type ExpectedBlockTime = ConstU64<1>; type EpochChangeTrigger = SameAuthoritiesForever; type MaxAuthorities = ConstU32<10>; type MaxTickets = ConstU32;