pallet_subspace/
lib.rs

1#![doc = include_str!("../README.md")]
2#![cfg_attr(not(feature = "std"), no_std)]
3#![feature(array_chunks, assert_matches, portable_simd)]
4#![warn(unused_must_use, unsafe_code, unused_variables)]
5
6#[cfg(not(feature = "std"))]
7extern crate alloc;
8
9#[cfg(test)]
10mod mock;
11#[cfg(test)]
12mod tests;
13
14#[cfg(feature = "runtime-benchmarks")]
15mod benchmarking;
16
17pub mod extensions;
18pub mod weights;
19
20use crate::extensions::weights::WeightInfo as ExtensionWeightInfo;
21#[cfg(not(feature = "std"))]
22use alloc::string::String;
23use core::num::NonZeroU64;
24use frame_support::dispatch::DispatchResult;
25use frame_support::pallet_prelude::{EnsureOrigin, RuntimeDebug};
26use frame_support::traits::Get;
27use frame_system::offchain::SubmitTransaction;
28use frame_system::pallet_prelude::*;
29use log::{debug, error, warn};
30pub use pallet::*;
31use parity_scale_codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
32use scale_info::TypeInfo;
33use schnorrkel::SignatureError;
34use sp_consensus_slots::Slot;
35use sp_consensus_subspace::consensus::{is_proof_of_time_valid, verify_solution};
36use sp_consensus_subspace::digests::CompatibleDigestItem;
37use sp_consensus_subspace::{
38    PotParameters, PotParametersChange, SignedVote, Vote, WrappedPotOutput,
39};
40use sp_runtime::Weight;
41use sp_runtime::generic::DigestItem;
42use sp_runtime::traits::{BlockNumberProvider, CheckedSub, Hash, One, Zero};
43use sp_runtime::transaction_validity::{
44    InvalidTransaction, TransactionPriority, TransactionSource, TransactionValidity,
45    TransactionValidityError, ValidTransaction,
46};
47use sp_std::collections::btree_map::BTreeMap;
48use sp_std::prelude::*;
49use subspace_core_primitives::pieces::PieceOffset;
50use subspace_core_primitives::sectors::{SectorId, SectorIndex};
51use subspace_core_primitives::segments::{
52    ArchivedHistorySegment, HistorySize, SegmentHeader, SegmentIndex,
53};
54use subspace_core_primitives::solutions::{RewardSignature, SolutionRange};
55use subspace_core_primitives::{
56    BlockHash, PublicKey, REWARD_SIGNING_CONTEXT, ScalarBytes, SlotNumber,
57};
58use subspace_runtime_primitives::CreateUnsigned;
59use subspace_verification::{
60    PieceCheckParams, VerifySolutionParams, check_reward_signature, derive_next_solution_range,
61    derive_pot_entropy,
62};
63pub use weights::WeightInfo;
64
65/// Trigger an era change, if any should take place.
66pub trait EraChangeTrigger {
67    /// Trigger an era change, if any should take place. This should be called
68    /// during every block, after initialization is done.
69    fn trigger<T: Config>(block_number: BlockNumberFor<T>);
70}
71
72/// A type signifying to Subspace that it should perform era changes with an internal trigger.
73pub struct NormalEraChange;
74
75impl EraChangeTrigger for NormalEraChange {
76    fn trigger<T: Config>(block_number: BlockNumberFor<T>) {
77        if <Pallet<T>>::should_era_change(block_number) {
78            <Pallet<T>>::enact_era_change();
79        }
80    }
81}
82
83/// Custom origin for validated unsigned extrinsics.
84#[derive(
85    PartialEq,
86    Eq,
87    Clone,
88    Encode,
89    Decode,
90    RuntimeDebug,
91    TypeInfo,
92    MaxEncodedLen,
93    DecodeWithMemTracking,
94)]
95pub enum RawOrigin {
96    ValidatedUnsigned,
97}
98
99/// Ensure the subspace origin.
100pub struct EnsureSubspaceOrigin;
101impl<O: Into<Result<RawOrigin, O>> + From<RawOrigin>> EnsureOrigin<O> for EnsureSubspaceOrigin {
102    type Success = ();
103
104    fn try_origin(o: O) -> Result<Self::Success, O> {
105        o.into().map(|o| match o {
106            RawOrigin::ValidatedUnsigned => (),
107        })
108    }
109
110    #[cfg(feature = "runtime-benchmarks")]
111    fn try_successful_origin() -> Result<O, ()> {
112        Ok(O::from(RawOrigin::ValidatedUnsigned))
113    }
114}
115
116#[derive(Copy, Clone, Eq, PartialEq, Encode, Decode, MaxEncodedLen, TypeInfo)]
117struct VoteVerificationData {
118    /// Block solution range, vote must not reach it
119    solution_range: SolutionRange,
120    vote_solution_range: SolutionRange,
121    current_slot: Slot,
122    parent_slot: Slot,
123}
124
125#[frame_support::pallet]
126pub mod pallet {
127    use super::{EraChangeTrigger, ExtensionWeightInfo, VoteVerificationData};
128    use crate::RawOrigin;
129    use crate::weights::WeightInfo;
130    use frame_support::pallet_prelude::*;
131    use frame_system::pallet_prelude::*;
132    use sp_consensus_slots::Slot;
133    use sp_consensus_subspace::SignedVote;
134    use sp_consensus_subspace::digests::CompatibleDigestItem;
135    use sp_consensus_subspace::inherents::{INHERENT_IDENTIFIER, InherentError, InherentType};
136    use sp_runtime::DigestItem;
137    use sp_runtime::traits::One;
138    use sp_std::collections::btree_map::BTreeMap;
139    use sp_std::num::NonZeroU32;
140    use sp_std::prelude::*;
141    use subspace_core_primitives::hashes::Blake3Hash;
142    use subspace_core_primitives::pieces::PieceOffset;
143    use subspace_core_primitives::pot::PotCheckpoints;
144    use subspace_core_primitives::sectors::SectorIndex;
145    use subspace_core_primitives::segments::{HistorySize, SegmentHeader, SegmentIndex};
146    use subspace_core_primitives::solutions::{RewardSignature, SolutionRange};
147    use subspace_core_primitives::{PublicKey, Randomness, ScalarBytes};
148
149    pub(super) struct InitialSolutionRanges<T: Config> {
150        _config: T,
151    }
152
153    impl<T: Config> Get<sp_consensus_subspace::SolutionRanges> for InitialSolutionRanges<T> {
154        fn get() -> sp_consensus_subspace::SolutionRanges {
155            sp_consensus_subspace::SolutionRanges {
156                current: T::InitialSolutionRange::get(),
157                next: None,
158                voting_current: if T::ShouldAdjustSolutionRange::get() {
159                    T::InitialSolutionRange::get()
160                        .saturating_mul(u64::from(T::ExpectedVotesPerBlock::get()) + 1)
161                } else {
162                    T::InitialSolutionRange::get()
163                },
164                voting_next: None,
165            }
166        }
167    }
168
169    /// Override for next solution range adjustment
170    #[derive(Debug, Encode, Decode, TypeInfo)]
171    pub(super) struct SolutionRangeOverride {
172        /// Value that should be set as solution range
173        pub(super) solution_range: SolutionRange,
174        /// Value that should be set as voting solution range
175        pub(super) voting_solution_range: SolutionRange,
176    }
177
178    /// The Subspace Pallet
179    #[pallet::pallet]
180    #[pallet::without_storage_info]
181    pub struct Pallet<T>(_);
182
183    #[pallet::config]
184    #[pallet::disable_frame_system_supertrait_check]
185    pub trait Config: frame_system::Config {
186        /// The overarching event type.
187        type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
188
189        /// Origin for subspace call.
190        type SubspaceOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = ()>;
191
192        /// Number of slots between slot arrival and when corresponding block can be produced.
193        ///
194        /// Practically this means future proof of time proof needs to be revealed this many slots
195        /// ahead before block can be authored even though solution is available before that.
196        #[pallet::constant]
197        type BlockAuthoringDelay: Get<Slot>;
198
199        /// Interval, in blocks, between blockchain entropy injection into proof of time chain.
200        #[pallet::constant]
201        type PotEntropyInjectionInterval: Get<BlockNumberFor<Self>>;
202
203        /// Interval, in entropy injection intervals, where to take entropy for injection from.
204        #[pallet::constant]
205        type PotEntropyInjectionLookbackDepth: Get<u8>;
206
207        /// Delay after block, in slots, when entropy injection takes effect.
208        #[pallet::constant]
209        type PotEntropyInjectionDelay: Get<Slot>;
210
211        /// The amount of time, in blocks, that each era should last.
212        /// NOTE: Currently it is not possible to change the era duration after
213        /// the chain has started. Attempting to do so will brick block production.
214        #[pallet::constant]
215        type EraDuration: Get<BlockNumberFor<Self>>;
216
217        /// Initial solution range used for challenges during the very first era.
218        #[pallet::constant]
219        type InitialSolutionRange: Get<SolutionRange>;
220
221        /// How often in slots slots (on average, not counting collisions) will have a block.
222        ///
223        /// Expressed as a rational where the first member of the tuple is the
224        /// numerator and the second is the denominator. The rational should
225        /// represent a value between 0 and 1.
226        #[pallet::constant]
227        type SlotProbability: Get<(u64, u64)>;
228
229        /// Depth `K` after which a block enters the recorded history (a global constant, as opposed
230        /// to the client-dependent transaction confirmation depth `k`).
231        #[pallet::constant]
232        type ConfirmationDepthK: Get<BlockNumberFor<Self>>;
233
234        /// Number of latest archived segments that are considered "recent history".
235        #[pallet::constant]
236        type RecentSegments: Get<HistorySize>;
237
238        /// Fraction of pieces from the "recent history" (`recent_segments`) in each sector.
239        #[pallet::constant]
240        type RecentHistoryFraction: Get<(HistorySize, HistorySize)>;
241
242        /// Minimum lifetime of a plotted sector, measured in archived segment.
243        #[pallet::constant]
244        type MinSectorLifetime: Get<HistorySize>;
245
246        /// Number of votes expected per block.
247        ///
248        /// This impacts solution range for votes in consensus.
249        #[pallet::constant]
250        type ExpectedVotesPerBlock: Get<u32>;
251
252        /// How many pieces one sector is supposed to contain (max)
253        #[pallet::constant]
254        type MaxPiecesInSector: Get<u16>;
255
256        type ShouldAdjustSolutionRange: Get<bool>;
257        /// Subspace requires some logic to be triggered on every block to query for whether an era
258        /// has ended and to perform the transition to the next era.
259        ///
260        /// Era is normally used to update solution range used for challenges.
261        type EraChangeTrigger: EraChangeTrigger;
262
263        /// Weight information for extrinsics in this pallet.
264        type WeightInfo: WeightInfo;
265
266        /// Maximum number of block number to block slot mappings to keep (oldest pruned first).
267        #[pallet::constant]
268        type BlockSlotCount: Get<u32>;
269
270        /// Extension weight information for the pallet's extensions.
271        type ExtensionWeightInfo: ExtensionWeightInfo;
272    }
273
274    #[derive(Debug, Default, Encode, Decode, TypeInfo)]
275    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
276    pub enum AllowAuthoringBy {
277        /// Anyone can author new blocks at genesis.
278        #[default]
279        Anyone,
280        /// Author of the first block will be able to author blocks going forward unless unlocked
281        /// for everyone.
282        FirstFarmer,
283        /// Specified root farmer is allowed to author blocks unless unlocked for everyone.
284        RootFarmer(PublicKey),
285    }
286
287    #[derive(Debug, Copy, Clone, Encode, Decode, TypeInfo)]
288    pub(super) struct PotEntropyValue {
289        /// Target slot at which entropy should be injected (when known)
290        pub(super) target_slot: Option<Slot>,
291        pub(super) entropy: Blake3Hash,
292    }
293
294    #[derive(Debug, Copy, Clone, Encode, Decode, TypeInfo, PartialEq)]
295    pub(super) struct PotSlotIterationsValue {
296        pub(super) slot_iterations: NonZeroU32,
297        /// Scheduled proof of time slot iterations update
298        pub(super) update: Option<PotSlotIterationsUpdate>,
299    }
300
301    #[derive(Debug, Copy, Clone, Encode, Decode, TypeInfo, PartialEq)]
302    pub(super) struct PotSlotIterationsUpdate {
303        /// Target slot at which entropy should be injected (when known)
304        pub(super) target_slot: Option<Slot>,
305        pub(super) slot_iterations: NonZeroU32,
306    }
307
308    /// When to enable block/vote rewards
309    #[derive(
310        Debug, Copy, Clone, Eq, PartialEq, Encode, Decode, TypeInfo, DecodeWithMemTracking,
311    )]
312    #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
313    pub enum EnableRewardsAt<BlockNumber> {
314        /// At specified height or next block if `None`
315        Height(BlockNumber),
316        /// When solution range is below specified threshold
317        SolutionRange(u64),
318        /// Manually with an explicit extrinsic
319        Manually,
320    }
321
322    #[pallet::genesis_config]
323    pub struct GenesisConfig<T>
324    where
325        T: Config,
326    {
327        /// When rewards should be enabled.
328        pub enable_rewards_at: EnableRewardsAt<BlockNumberFor<T>>,
329        /// Who can author blocks at genesis.
330        pub allow_authoring_by: AllowAuthoringBy,
331        /// Number of iterations for proof of time per slot
332        pub pot_slot_iterations: NonZeroU32,
333        #[serde(skip)]
334        pub phantom: PhantomData<T>,
335    }
336
337    impl<T> Default for GenesisConfig<T>
338    where
339        T: Config,
340    {
341        #[inline]
342        fn default() -> Self {
343            Self {
344                enable_rewards_at: EnableRewardsAt::Height(BlockNumberFor::<T>::one()),
345                allow_authoring_by: AllowAuthoringBy::Anyone,
346                pot_slot_iterations: NonZeroU32::MIN,
347                phantom: PhantomData,
348            }
349        }
350    }
351
352    #[pallet::genesis_build]
353    impl<T> BuildGenesisConfig for GenesisConfig<T>
354    where
355        T: Config,
356    {
357        fn build(&self) {
358            match self.enable_rewards_at {
359                EnableRewardsAt::Height(block_number) => {
360                    EnableRewards::<T>::put(block_number);
361                }
362                EnableRewardsAt::SolutionRange(solution_range) => {
363                    EnableRewardsBelowSolutionRange::<T>::put(solution_range);
364                }
365                EnableRewardsAt::Manually => {
366                    // Nothing to do in this case
367                }
368            }
369            match &self.allow_authoring_by {
370                AllowAuthoringBy::Anyone => {
371                    AllowAuthoringByAnyone::<T>::put(true);
372                }
373                AllowAuthoringBy::FirstFarmer => {
374                    AllowAuthoringByAnyone::<T>::put(false);
375                }
376                AllowAuthoringBy::RootFarmer(root_farmer) => {
377                    AllowAuthoringByAnyone::<T>::put(false);
378                    RootPlotPublicKey::<T>::put(root_farmer);
379                }
380            }
381            PotSlotIterations::<T>::put(PotSlotIterationsValue {
382                slot_iterations: self.pot_slot_iterations,
383                update: None,
384            });
385        }
386    }
387
388    /// Events type.
389    #[pallet::event]
390    #[pallet::generate_deposit(pub (super) fn deposit_event)]
391    pub enum Event<T: Config> {
392        /// Segment header was stored in blockchain history.
393        SegmentHeaderStored { segment_header: SegmentHeader },
394        /// Farmer vote.
395        FarmerVote {
396            public_key: PublicKey,
397            reward_address: T::AccountId,
398            height: BlockNumberFor<T>,
399            parent_hash: T::Hash,
400        },
401    }
402
403    #[pallet::origin]
404    pub type Origin = RawOrigin;
405
406    #[pallet::error]
407    pub enum Error<T> {
408        /// Solution range adjustment already enabled.
409        SolutionRangeAdjustmentAlreadyEnabled,
410        /// Iterations are not multiple of number of checkpoints times two
411        NotMultipleOfCheckpoints,
412        /// Proof of time slot iterations must increase as hardware improves
413        PotSlotIterationsMustIncrease,
414        /// Proof of time slot iterations update already scheduled
415        PotSlotIterationsUpdateAlreadyScheduled,
416    }
417
418    /// Bounded mapping from block number to slot
419    #[pallet::storage]
420    #[pallet::getter(fn block_slots)]
421    pub(super) type BlockSlots<T: Config> =
422        StorageValue<_, BoundedBTreeMap<BlockNumberFor<T>, Slot, T::BlockSlotCount>, ValueQuery>;
423
424    /// Solution ranges used for challenges.
425    #[pallet::storage]
426    #[pallet::getter(fn solution_ranges)]
427    pub(super) type SolutionRanges<T: Config> = StorageValue<
428        _,
429        sp_consensus_subspace::SolutionRanges,
430        ValueQuery,
431        InitialSolutionRanges<T>,
432    >;
433
434    /// Storage to check if the solution range is to be adjusted for next era
435    #[pallet::storage]
436    #[pallet::getter(fn should_adjust_solution_range)]
437    pub(super) type ShouldAdjustSolutionRange<T: Config> =
438        StorageValue<_, bool, ValueQuery, T::ShouldAdjustSolutionRange>;
439
440    /// Override solution range during next update
441    #[pallet::storage]
442    pub(super) type NextSolutionRangeOverride<T> = StorageValue<_, SolutionRangeOverride>;
443
444    /// Slot at which current era started.
445    #[pallet::storage]
446    pub(super) type EraStartSlot<T> = StorageValue<_, Slot>;
447
448    /// Mapping from segment index to corresponding segment commitment of contained records.
449    #[pallet::storage]
450    #[pallet::getter(fn segment_commitment)]
451    pub(super) type SegmentCommitment<T> = CountedStorageMap<
452        _,
453        Twox64Concat,
454        SegmentIndex,
455        subspace_core_primitives::segments::SegmentCommitment,
456    >;
457
458    /// Whether the segment headers inherent has been processed in this block (temporary value).
459    ///
460    /// This value is updated to `true` when processing `store_segment_headers` by a node.
461    /// It is then cleared at the end of each block execution in the `on_finalize` hook.
462    #[pallet::storage]
463    pub(super) type DidProcessSegmentHeaders<T: Config> = StorageValue<_, bool, ValueQuery>;
464
465    /// Storage of previous vote verification data, updated on each block during finalization.
466    #[pallet::storage]
467    pub(super) type ParentVoteVerificationData<T> = StorageValue<_, VoteVerificationData>;
468
469    /// Parent block author information.
470    #[pallet::storage]
471    pub(super) type ParentBlockAuthorInfo<T> =
472        StorageValue<_, (PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot)>;
473
474    /// Enable rewards since specified block number.
475    #[pallet::storage]
476    pub(super) type EnableRewards<T: Config> = StorageValue<_, BlockNumberFor<T>>;
477
478    /// Enable rewards when solution range is below this threshold.
479    #[pallet::storage]
480    pub(super) type EnableRewardsBelowSolutionRange<T: Config> = StorageValue<_, u64>;
481
482    /// Block author information
483    #[pallet::storage]
484    pub(super) type CurrentBlockAuthorInfo<T: Config> = StorageValue<
485        _,
486        (
487            PublicKey,
488            SectorIndex,
489            PieceOffset,
490            ScalarBytes,
491            Slot,
492            Option<T::AccountId>,
493        ),
494    >;
495
496    /// Voters in the parent block (set at the end of the block with current values).
497    #[pallet::storage]
498    pub(super) type ParentBlockVoters<T: Config> = StorageValue<
499        _,
500        BTreeMap<
501            (PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot),
502            (Option<T::AccountId>, RewardSignature),
503        >,
504        ValueQuery,
505    >;
506
507    /// Voters in the current block thus far
508    #[pallet::storage]
509    pub(super) type CurrentBlockVoters<T: Config> = StorageValue<
510        _,
511        BTreeMap<
512            (PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot),
513            (Option<T::AccountId>, RewardSignature),
514        >,
515    >;
516
517    /// Number of iterations for proof of time per slot with optional scheduled update
518    #[pallet::storage]
519    pub(super) type PotSlotIterations<T> = StorageValue<_, PotSlotIterationsValue>;
520
521    /// Entropy that needs to be injected into proof of time chain at specific slot associated with
522    /// block number it came from.
523    #[pallet::storage]
524    pub(super) type PotEntropy<T: Config> =
525        StorageValue<_, BTreeMap<BlockNumberFor<T>, PotEntropyValue>, ValueQuery>;
526
527    /// The current block randomness, updated at block initialization. When the proof of time feature
528    /// is enabled it derived from PoT otherwise PoR.
529    #[pallet::storage]
530    pub type BlockRandomness<T> = StorageValue<_, Randomness>;
531
532    /// Allow block authoring by anyone or just root.
533    #[pallet::storage]
534    pub(super) type AllowAuthoringByAnyone<T> = StorageValue<_, bool, ValueQuery>;
535
536    /// Root plot public key.
537    ///
538    /// Set just once to make sure no one else can author blocks until allowed for anyone.
539    #[pallet::storage]
540    #[pallet::getter(fn root_plot_public_key)]
541    pub(super) type RootPlotPublicKey<T> = StorageValue<_, PublicKey>;
542
543    #[pallet::hooks]
544    impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {
545        fn on_initialize(block_number: BlockNumberFor<T>) -> Weight {
546            Self::do_initialize(block_number);
547            Weight::zero()
548        }
549
550        fn on_finalize(block_number: BlockNumberFor<T>) {
551            Self::do_finalize(block_number)
552        }
553    }
554
555    #[pallet::call]
556    impl<T: Config> Pallet<T> {
557        /// Submit new segment header to the blockchain. This is an inherent extrinsic and part of
558        /// the Subspace consensus logic.
559        #[pallet::call_index(0)]
560        #[pallet::weight((< T as Config >::WeightInfo::store_segment_headers(segment_headers.len() as u32), DispatchClass::Mandatory))]
561        pub fn store_segment_headers(
562            origin: OriginFor<T>,
563            segment_headers: Vec<SegmentHeader>,
564        ) -> DispatchResult {
565            ensure_none(origin)?;
566            Self::do_store_segment_headers(segment_headers)
567        }
568
569        /// Enable solution range adjustment after every era.
570        /// Note: No effect on the solution range for the current era
571        #[pallet::call_index(1)]
572        #[pallet::weight(< T as Config >::WeightInfo::enable_solution_range_adjustment())]
573        pub fn enable_solution_range_adjustment(
574            origin: OriginFor<T>,
575            solution_range_override: Option<u64>,
576            voting_solution_range_override: Option<u64>,
577        ) -> DispatchResult {
578            ensure_root(origin)?;
579
580            Self::do_enable_solution_range_adjustment(
581                solution_range_override,
582                voting_solution_range_override,
583            )?;
584
585            frame_system::Pallet::<T>::deposit_log(
586                DigestItem::enable_solution_range_adjustment_and_override(solution_range_override),
587            );
588
589            Ok(())
590        }
591
592        /// Farmer vote, currently only used for extra rewards to farmers.
593        #[pallet::call_index(2)]
594        #[pallet::weight((< T as Config >::WeightInfo::vote(), DispatchClass::Operational))]
595        // Suppression because the custom syntax will also generate an enum and we need enum to have
596        // boxed value.
597        #[allow(clippy::boxed_local)]
598        pub fn vote(
599            origin: OriginFor<T>,
600            signed_vote: Box<SignedVote<BlockNumberFor<T>, T::Hash, T::AccountId>>,
601        ) -> DispatchResult {
602            T::SubspaceOrigin::ensure_origin(origin)?;
603
604            Self::do_vote(*signed_vote)
605        }
606
607        /// Enable rewards for blocks and votes at specified block height.
608        #[pallet::call_index(3)]
609        #[pallet::weight(< T as Config >::WeightInfo::enable_rewards_at())]
610        pub fn enable_rewards_at(
611            origin: OriginFor<T>,
612            enable_rewards_at: EnableRewardsAt<BlockNumberFor<T>>,
613        ) -> DispatchResult {
614            ensure_root(origin)?;
615
616            Self::do_enable_rewards_at(enable_rewards_at)
617        }
618
619        /// Enable storage access for all users.
620        #[pallet::call_index(4)]
621        #[pallet::weight(< T as Config >::WeightInfo::enable_authoring_by_anyone())]
622        pub fn enable_authoring_by_anyone(origin: OriginFor<T>) -> DispatchResult {
623            ensure_root(origin)?;
624
625            AllowAuthoringByAnyone::<T>::put(true);
626            RootPlotPublicKey::<T>::take();
627            // Deposit root plot public key update such that light client can validate blocks later.
628            frame_system::Pallet::<T>::deposit_log(DigestItem::root_plot_public_key_update(None));
629
630            Ok(())
631        }
632
633        /// Update proof of time slot iterations
634        #[pallet::call_index(5)]
635        #[pallet::weight(< T as Config >::WeightInfo::set_pot_slot_iterations())]
636        pub fn set_pot_slot_iterations(
637            origin: OriginFor<T>,
638            slot_iterations: NonZeroU32,
639        ) -> DispatchResult {
640            ensure_root(origin)?;
641
642            if slot_iterations.get() % u32::from(PotCheckpoints::NUM_CHECKPOINTS.get() * 2) != 0 {
643                return Err(Error::<T>::NotMultipleOfCheckpoints.into());
644            }
645
646            let mut pot_slot_iterations =
647                PotSlotIterations::<T>::get().expect("Always initialized during genesis; qed");
648
649            if pot_slot_iterations.slot_iterations >= slot_iterations {
650                return Err(Error::<T>::PotSlotIterationsMustIncrease.into());
651            }
652
653            // Can't update if already scheduled since it will cause verification issues
654            if let Some(pot_slot_iterations_update_value) = pot_slot_iterations.update
655                && pot_slot_iterations_update_value.target_slot.is_some()
656            {
657                return Err(Error::<T>::PotSlotIterationsUpdateAlreadyScheduled.into());
658            }
659
660            pot_slot_iterations.update.replace(PotSlotIterationsUpdate {
661                // Slot will be known later when next entropy injection takes place
662                target_slot: None,
663                slot_iterations,
664            });
665
666            PotSlotIterations::<T>::put(pot_slot_iterations);
667
668            Ok(())
669        }
670    }
671
672    #[pallet::inherent]
673    impl<T: Config> ProvideInherent for Pallet<T> {
674        type Call = Call<T>;
675        type Error = InherentError;
676        const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
677
678        fn create_inherent(data: &InherentData) -> Option<Self::Call> {
679            let inherent_data = data
680                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
681                .expect("Subspace inherent data not correctly encoded")
682                .expect("Subspace inherent data must be provided");
683
684            let segment_headers = inherent_data.segment_headers;
685            if segment_headers.is_empty() {
686                None
687            } else {
688                Some(Call::store_segment_headers { segment_headers })
689            }
690        }
691
692        fn is_inherent_required(data: &InherentData) -> Result<Option<Self::Error>, Self::Error> {
693            let inherent_data = data
694                .get_data::<InherentType>(&INHERENT_IDENTIFIER)
695                .expect("Subspace inherent data not correctly encoded")
696                .expect("Subspace inherent data must be provided");
697
698            Ok(if inherent_data.segment_headers.is_empty() {
699                None
700            } else {
701                Some(InherentError::MissingSegmentHeadersList)
702            })
703        }
704
705        fn check_inherent(call: &Self::Call, data: &InherentData) -> Result<(), Self::Error> {
706            if let Call::store_segment_headers { segment_headers } = call {
707                let inherent_data = data
708                    .get_data::<InherentType>(&INHERENT_IDENTIFIER)
709                    .expect("Subspace inherent data not correctly encoded")
710                    .expect("Subspace inherent data must be provided");
711
712                if segment_headers != &inherent_data.segment_headers {
713                    return Err(InherentError::IncorrectSegmentHeadersList {
714                        expected: inherent_data.segment_headers,
715                        actual: segment_headers.clone(),
716                    });
717                }
718            }
719
720            Ok(())
721        }
722
723        fn is_inherent(call: &Self::Call) -> bool {
724            matches!(call, Call::store_segment_headers { .. })
725        }
726    }
727
728    #[pallet::validate_unsigned]
729    impl<T: Config> ValidateUnsigned for Pallet<T> {
730        type Call = Call<T>;
731        fn validate_unsigned(source: TransactionSource, call: &Self::Call) -> TransactionValidity {
732            match call {
733                Call::store_segment_headers { segment_headers } => {
734                    Self::validate_segment_header(source, segment_headers)
735                }
736                _ => InvalidTransaction::Call.into(),
737            }
738        }
739
740        fn pre_dispatch(call: &Self::Call) -> Result<(), TransactionValidityError> {
741            match call {
742                Call::store_segment_headers { segment_headers } => {
743                    Self::pre_dispatch_segment_header(segment_headers)
744                }
745                _ => Err(InvalidTransaction::Call.into()),
746            }
747        }
748    }
749}
750
751impl<T: Config> Pallet<T> {
752    /// Total number of pieces in the blockchain
753    pub fn history_size() -> HistorySize {
754        // Chain starts with one segment plotted, even if it is not recorded in the runtime yet
755        let number_of_segments = u64::from(SegmentCommitment::<T>::count()).max(1);
756        HistorySize::from(NonZeroU64::new(number_of_segments).expect("Not zero; qed"))
757    }
758
759    /// Determine whether an era change should take place at this block.
760    /// Assumes that initialization has already taken place.
761    fn should_era_change(block_number: BlockNumberFor<T>) -> bool {
762        block_number % T::EraDuration::get() == Zero::zero()
763    }
764
765    /// DANGEROUS: Enact era change. Should be done on every block where `should_era_change` has
766    /// returned `true`, and the caller is the only caller of this function.
767    ///
768    /// This will update solution range used in consensus.
769    fn enact_era_change() {
770        let slot_probability = T::SlotProbability::get();
771
772        let current_slot = Self::current_slot();
773
774        SolutionRanges::<T>::mutate(|solution_ranges| {
775            let next_solution_range;
776            let next_voting_solution_range;
777            // Check if the solution range should be adjusted for next era.
778            if !ShouldAdjustSolutionRange::<T>::get() {
779                next_solution_range = solution_ranges.current;
780                next_voting_solution_range = solution_ranges.current;
781            } else if let Some(solution_range_override) = NextSolutionRangeOverride::<T>::take() {
782                next_solution_range = solution_range_override.solution_range;
783                next_voting_solution_range = solution_range_override.voting_solution_range;
784            } else {
785                next_solution_range = derive_next_solution_range(
786                    // If Era start slot is not found it means we have just finished the first era
787                    u64::from(EraStartSlot::<T>::get().unwrap_or_default()),
788                    u64::from(current_slot),
789                    slot_probability,
790                    solution_ranges.current,
791                    T::EraDuration::get()
792                        .try_into()
793                        .unwrap_or_else(|_| panic!("Era duration is always within u64; qed")),
794                );
795
796                next_voting_solution_range = next_solution_range
797                    .saturating_mul(u64::from(T::ExpectedVotesPerBlock::get()) + 1);
798            };
799            solution_ranges.next.replace(next_solution_range);
800            solution_ranges
801                .voting_next
802                .replace(next_voting_solution_range);
803
804            if let Some(solution_range_for_rewards) = EnableRewardsBelowSolutionRange::<T>::get()
805                && next_solution_range <= solution_range_for_rewards
806            {
807                EnableRewardsBelowSolutionRange::<T>::take();
808
809                let next_block_number =
810                    frame_system::Pallet::<T>::current_block_number() + One::one();
811                EnableRewards::<T>::put(next_block_number);
812            }
813        });
814
815        EraStartSlot::<T>::put(current_slot);
816    }
817
818    fn do_initialize(block_number: BlockNumberFor<T>) {
819        let pre_digest = frame_system::Pallet::<T>::digest()
820            .logs
821            .iter()
822            .find_map(|s| s.as_subspace_pre_digest::<T::AccountId>())
823            .expect("Block must always have pre-digest");
824        let current_slot = pre_digest.slot();
825
826        BlockSlots::<T>::mutate(|block_slots| {
827            if let Some(to_remove) = block_number.checked_sub(&T::BlockSlotCount::get().into()) {
828                block_slots.remove(&to_remove);
829            }
830            block_slots
831                .try_insert(block_number, current_slot)
832                .expect("one entry just removed before inserting; qed");
833        });
834
835        {
836            // Remove old value
837            CurrentBlockAuthorInfo::<T>::take();
838            let farmer_public_key = pre_digest.solution().public_key;
839
840            // Optional restriction for block authoring to the root user
841            if !AllowAuthoringByAnyone::<T>::get() {
842                RootPlotPublicKey::<T>::mutate(|maybe_root_plot_public_key| {
843                    if let Some(root_plot_public_key) = maybe_root_plot_public_key {
844                        if root_plot_public_key != &farmer_public_key {
845                            panic!("Client bug, authoring must be only done by the root user");
846                        }
847                    } else {
848                        maybe_root_plot_public_key.replace(farmer_public_key);
849                        // Deposit root plot public key update such that light client can validate
850                        // blocks later.
851                        frame_system::Pallet::<T>::deposit_log(
852                            DigestItem::root_plot_public_key_update(Some(farmer_public_key)),
853                        );
854                    }
855                });
856            }
857
858            let key = (
859                farmer_public_key,
860                pre_digest.solution().sector_index,
861                pre_digest.solution().piece_offset,
862                pre_digest.solution().chunk,
863                current_slot,
864            );
865            if !ParentBlockVoters::<T>::get().contains_key(&key) {
866                let (public_key, sector_index, piece_offset, chunk, slot) = key;
867
868                CurrentBlockAuthorInfo::<T>::put((
869                    public_key,
870                    sector_index,
871                    piece_offset,
872                    chunk,
873                    slot,
874                    Some(pre_digest.solution().reward_address.clone()),
875                ));
876            }
877        }
878        CurrentBlockVoters::<T>::put(BTreeMap::<
879            (PublicKey, SectorIndex, PieceOffset, ScalarBytes, Slot),
880            (Option<T::AccountId>, RewardSignature),
881        >::default());
882
883        // If solution range was updated in previous block, set it as current.
884        if let sp_consensus_subspace::SolutionRanges {
885            next: Some(next),
886            voting_next: Some(voting_next),
887            ..
888        } = SolutionRanges::<T>::get()
889        {
890            SolutionRanges::<T>::put(sp_consensus_subspace::SolutionRanges {
891                current: next,
892                next: None,
893                voting_current: voting_next,
894                voting_next: None,
895            });
896        }
897
898        let block_randomness = pre_digest
899            .pot_info()
900            .proof_of_time()
901            .derive_global_randomness();
902
903        // Update the block randomness.
904        BlockRandomness::<T>::put(block_randomness);
905
906        // Deposit solution range data such that light client can validate blocks later.
907        frame_system::Pallet::<T>::deposit_log(DigestItem::solution_range(
908            SolutionRanges::<T>::get().current,
909        ));
910
911        // Enact era change, if necessary.
912        T::EraChangeTrigger::trigger::<T>(block_number);
913
914        {
915            let mut pot_slot_iterations =
916                PotSlotIterations::<T>::get().expect("Always initialized during genesis; qed");
917            // This is what we had after previous block
918            frame_system::Pallet::<T>::deposit_log(DigestItem::pot_slot_iterations(
919                pot_slot_iterations.slot_iterations,
920            ));
921
922            // Check PoT slot iterations update and apply it if it is time to do so, while also
923            // removing corresponding storage item
924            if let Some(update) = pot_slot_iterations.update
925                && let Some(target_slot) = update.target_slot
926                && target_slot <= current_slot
927            {
928                debug!(
929                    "Applying PoT slots update, changing to {} at block #{:?}",
930                    update.slot_iterations, block_number
931                );
932                pot_slot_iterations = PotSlotIterationsValue {
933                    slot_iterations: update.slot_iterations,
934                    update: None,
935                };
936                PotSlotIterations::<T>::put(pot_slot_iterations);
937            }
938            let pot_entropy_injection_interval = T::PotEntropyInjectionInterval::get();
939            let pot_entropy_injection_delay = T::PotEntropyInjectionDelay::get();
940
941            let mut entropy = PotEntropy::<T>::get();
942            let lookback_in_blocks = pot_entropy_injection_interval
943                * BlockNumberFor::<T>::from(T::PotEntropyInjectionLookbackDepth::get());
944            let last_entropy_injection_block =
945                block_number / pot_entropy_injection_interval * pot_entropy_injection_interval;
946            let maybe_entropy_source_block_number =
947                last_entropy_injection_block.checked_sub(&lookback_in_blocks);
948
949            if (block_number % pot_entropy_injection_interval).is_zero() {
950                let current_block_entropy = derive_pot_entropy(
951                    &pre_digest.solution().chunk,
952                    pre_digest.pot_info().proof_of_time(),
953                );
954                // Collect entropy every `T::PotEntropyInjectionInterval` blocks
955                entropy.insert(
956                    block_number,
957                    PotEntropyValue {
958                        target_slot: None,
959                        entropy: current_block_entropy,
960                    },
961                );
962
963                // Update target slot for entropy injection once we know it
964                if let Some(entropy_source_block_number) = maybe_entropy_source_block_number
965                    && let Some(entropy_value) = entropy.get_mut(&entropy_source_block_number)
966                {
967                    let target_slot = pre_digest
968                        .slot()
969                        .saturating_add(pot_entropy_injection_delay);
970                    debug!("Pot entropy injection will happen at slot {target_slot:?}",);
971                    entropy_value.target_slot.replace(target_slot);
972
973                    // Schedule PoT slot iterations update at the same slot as entropy
974                    if let Some(update) = &mut pot_slot_iterations.update
975                        && update.target_slot.is_none()
976                    {
977                        debug!("Scheduling PoT slots update to happen at slot {target_slot:?}");
978                        update.target_slot.replace(target_slot);
979                        PotSlotIterations::<T>::put(pot_slot_iterations);
980                    }
981                }
982
983                PotEntropy::<T>::put(entropy.clone());
984            }
985
986            // Deposit consensus log item with parameters change in case corresponding entropy is
987            // available
988            if let Some(entropy_source_block_number) = maybe_entropy_source_block_number {
989                let maybe_entropy_value = entropy.get(&entropy_source_block_number).copied();
990                if let Some(PotEntropyValue {
991                    target_slot,
992                    entropy,
993                }) = maybe_entropy_value
994                {
995                    let target_slot = target_slot
996                        .expect("Target slot is guaranteed to be present due to logic above; qed");
997                    // Check if there was a PoT slot iterations update at the same exact slot
998                    let slot_iterations = if let Some(update) = pot_slot_iterations.update
999                        && let Some(update_target_slot) = update.target_slot
1000                        && update_target_slot == target_slot
1001                    {
1002                        debug!("Applying PoT slots update to the next PoT parameters change");
1003                        update.slot_iterations
1004                    } else {
1005                        pot_slot_iterations.slot_iterations
1006                    };
1007
1008                    frame_system::Pallet::<T>::deposit_log(DigestItem::pot_parameters_change(
1009                        PotParametersChange {
1010                            slot: target_slot,
1011                            slot_iterations,
1012                            entropy,
1013                        },
1014                    ));
1015                }
1016            }
1017
1018            // Clean up old values we'll no longer need
1019            if let Some(entry) = entropy.first_entry()
1020                && let Some(target_slot) = entry.get().target_slot
1021                && target_slot < current_slot
1022            {
1023                entry.remove();
1024                PotEntropy::<T>::put(entropy);
1025            }
1026        }
1027    }
1028
1029    fn do_finalize(_block_number: BlockNumberFor<T>) {
1030        // Deposit the next solution range in the block finalization to account for solution range override extrinsic and
1031        // era change happens in the same block.
1032        if let Some(next_solution_range) = SolutionRanges::<T>::get().next {
1033            // Deposit next solution range data such that light client can validate blocks later.
1034            frame_system::Pallet::<T>::deposit_log(DigestItem::next_solution_range(
1035                next_solution_range,
1036            ));
1037        }
1038
1039        if let Some((public_key, sector_index, piece_offset, scalar, slot, _reward_address)) =
1040            CurrentBlockAuthorInfo::<T>::get()
1041        {
1042            ParentBlockAuthorInfo::<T>::put((public_key, sector_index, piece_offset, scalar, slot));
1043        } else {
1044            ParentBlockAuthorInfo::<T>::take();
1045        }
1046
1047        ParentVoteVerificationData::<T>::put(current_vote_verification_data::<T>(true));
1048
1049        ParentBlockVoters::<T>::put(CurrentBlockVoters::<T>::get().unwrap_or_default());
1050
1051        DidProcessSegmentHeaders::<T>::take();
1052    }
1053
1054    fn do_store_segment_headers(segment_headers: Vec<SegmentHeader>) -> DispatchResult {
1055        assert!(
1056            !DidProcessSegmentHeaders::<T>::exists(),
1057            "Segment headers must be updated only once in the block"
1058        );
1059
1060        for segment_header in segment_headers {
1061            SegmentCommitment::<T>::insert(
1062                segment_header.segment_index(),
1063                segment_header.segment_commitment(),
1064            );
1065            // Deposit global randomness data such that light client can validate blocks later.
1066            frame_system::Pallet::<T>::deposit_log(DigestItem::segment_commitment(
1067                segment_header.segment_index(),
1068                segment_header.segment_commitment(),
1069            ));
1070            Self::deposit_event(Event::SegmentHeaderStored { segment_header });
1071        }
1072
1073        DidProcessSegmentHeaders::<T>::put(true);
1074        Ok(())
1075    }
1076
1077    fn do_enable_solution_range_adjustment(
1078        solution_range_override: Option<u64>,
1079        voting_solution_range_override: Option<u64>,
1080    ) -> DispatchResult {
1081        if ShouldAdjustSolutionRange::<T>::get() {
1082            return Err(Error::<T>::SolutionRangeAdjustmentAlreadyEnabled.into());
1083        }
1084
1085        ShouldAdjustSolutionRange::<T>::put(true);
1086
1087        if let Some(solution_range) = solution_range_override {
1088            let voting_solution_range = voting_solution_range_override.unwrap_or_else(|| {
1089                solution_range.saturating_mul(u64::from(T::ExpectedVotesPerBlock::get()) + 1)
1090            });
1091            SolutionRanges::<T>::mutate(|solution_ranges| {
1092                // If solution range update is already scheduled, just update values
1093                if solution_ranges.next.is_some() {
1094                    solution_ranges.next.replace(solution_range);
1095                    solution_ranges.voting_next.replace(voting_solution_range);
1096                } else {
1097                    solution_ranges.current = solution_range;
1098                    solution_ranges.voting_current = voting_solution_range;
1099
1100                    // Solution range can re-adjust very soon, make sure next re-adjustment is
1101                    // also overridden
1102                    NextSolutionRangeOverride::<T>::put(SolutionRangeOverride {
1103                        solution_range,
1104                        voting_solution_range,
1105                    });
1106                    frame_system::Pallet::<T>::deposit_log(DigestItem::next_solution_range(
1107                        solution_range,
1108                    ));
1109                }
1110            });
1111        }
1112
1113        Ok(())
1114    }
1115
1116    fn do_vote(
1117        signed_vote: SignedVote<BlockNumberFor<T>, T::Hash, T::AccountId>,
1118    ) -> DispatchResult {
1119        let Vote::V0 {
1120            height,
1121            parent_hash,
1122            solution,
1123            ..
1124        } = signed_vote.vote;
1125
1126        Self::deposit_event(Event::FarmerVote {
1127            public_key: solution.public_key,
1128            reward_address: solution.reward_address,
1129            height,
1130            parent_hash,
1131        });
1132
1133        Ok(())
1134    }
1135
1136    fn do_enable_rewards_at(
1137        enable_rewards_at: EnableRewardsAt<BlockNumberFor<T>>,
1138    ) -> DispatchResult {
1139        match enable_rewards_at {
1140            EnableRewardsAt::Height(block_number) => {
1141                // Enable rewards at a particular block height (default to the next block after
1142                // this)
1143                let next_block_number =
1144                    frame_system::Pallet::<T>::current_block_number() + One::one();
1145                EnableRewards::<T>::put(block_number.max(next_block_number));
1146            }
1147            EnableRewardsAt::SolutionRange(solution_range) => {
1148                EnableRewardsBelowSolutionRange::<T>::put(solution_range);
1149            }
1150            EnableRewardsAt::Manually => {
1151                // Nothing to do in this case
1152            }
1153        }
1154
1155        Ok(())
1156    }
1157
1158    /// Proof of time parameters
1159    pub fn pot_parameters() -> PotParameters {
1160        let block_number = frame_system::Pallet::<T>::block_number();
1161        let pot_slot_iterations =
1162            PotSlotIterations::<T>::get().expect("Always initialized during genesis; qed");
1163        let pot_entropy_injection_interval = T::PotEntropyInjectionInterval::get();
1164
1165        let entropy = PotEntropy::<T>::get();
1166        let lookback_in_blocks = pot_entropy_injection_interval
1167            * BlockNumberFor::<T>::from(T::PotEntropyInjectionLookbackDepth::get());
1168        let last_entropy_injection_block =
1169            block_number / pot_entropy_injection_interval * pot_entropy_injection_interval;
1170        let maybe_entropy_source_block_number =
1171            last_entropy_injection_block.checked_sub(&lookback_in_blocks);
1172
1173        let mut next_change = None;
1174
1175        if let Some(entropy_source_block_number) = maybe_entropy_source_block_number {
1176            let maybe_entropy_value = entropy.get(&entropy_source_block_number).copied();
1177            if let Some(PotEntropyValue {
1178                target_slot,
1179                entropy,
1180            }) = maybe_entropy_value
1181            {
1182                let target_slot = target_slot.expect(
1183                    "Always present due to identical check present in block initialization; qed",
1184                );
1185                // Check if there was a PoT slot iterations update at the same exact slot
1186                let slot_iterations = if let Some(update) = pot_slot_iterations.update
1187                    && let Some(update_target_slot) = update.target_slot
1188                    && update_target_slot == target_slot
1189                {
1190                    update.slot_iterations
1191                } else {
1192                    pot_slot_iterations.slot_iterations
1193                };
1194
1195                next_change.replace(PotParametersChange {
1196                    slot: target_slot,
1197                    slot_iterations,
1198                    entropy,
1199                });
1200            }
1201        }
1202
1203        PotParameters::V0 {
1204            slot_iterations: pot_slot_iterations.slot_iterations,
1205            next_change,
1206        }
1207    }
1208
1209    /// Current slot number
1210    pub fn current_slot() -> Slot {
1211        BlockSlots::<T>::get()
1212            .last_key_value()
1213            .map(|(_block, slot)| *slot)
1214            .unwrap_or_default()
1215    }
1216
1217    /// Size of the archived history of the blockchain in bytes
1218    pub fn archived_history_size() -> u64 {
1219        let archived_segments = SegmentCommitment::<T>::count();
1220
1221        u64::from(archived_segments) * ArchivedHistorySegment::SIZE as u64
1222    }
1223}
1224
1225impl<T> Pallet<T>
1226where
1227    T: Config + CreateUnsigned<Call<T>>,
1228{
1229    /// Submit farmer vote that is essentially a header with bigger solution range than
1230    /// acceptable for block authoring.
1231    pub fn submit_vote(signed_vote: SignedVote<BlockNumberFor<T>, T::Hash, T::AccountId>) {
1232        let call = Call::vote {
1233            signed_vote: Box::new(signed_vote),
1234        };
1235
1236        let ext = T::create_unsigned(call.into());
1237        match SubmitTransaction::<T, Call<T>>::submit_transaction(ext) {
1238            Ok(()) => {
1239                debug!("Submitted Subspace vote");
1240            }
1241            Err(()) => {
1242                error!("Error submitting Subspace vote");
1243            }
1244        }
1245    }
1246}
1247
1248/// Methods for the `ValidateUnsigned` implementation:
1249/// It restricts calls to `store_segment_header` to local calls (i.e. extrinsics generated on this
1250/// node) or that already in a block. This guarantees that only block authors can include root
1251/// blocks.
1252impl<T: Config> Pallet<T> {
1253    fn validate_segment_header(
1254        source: TransactionSource,
1255        segment_headers: &[SegmentHeader],
1256    ) -> TransactionValidity {
1257        // Discard segment header not coming from the local node
1258        if !matches!(
1259            source,
1260            TransactionSource::Local | TransactionSource::InBlock,
1261        ) {
1262            warn!("Rejecting segment header extrinsic because it is not local/in-block.",);
1263
1264            return InvalidTransaction::Call.into();
1265        }
1266
1267        check_segment_headers::<T>(segment_headers)?;
1268
1269        ValidTransaction::with_tag_prefix("SubspaceSegmentHeader")
1270            // We assign the maximum priority for any segment header.
1271            .priority(TransactionPriority::MAX)
1272            // Should be included immediately into the current block (this is an inherent
1273            // extrinsic) with no exceptions.
1274            .longevity(0)
1275            // We don't propagate this. This can never be included on a remote node.
1276            .propagate(false)
1277            .build()
1278    }
1279
1280    fn pre_dispatch_segment_header(
1281        segment_headers: &[SegmentHeader],
1282    ) -> Result<(), TransactionValidityError> {
1283        check_segment_headers::<T>(segment_headers)
1284    }
1285
1286    fn validate_vote(
1287        signed_vote: &SignedVote<BlockNumberFor<T>, T::Hash, T::AccountId>,
1288    ) -> Result<(ValidTransaction, Weight), TransactionValidityError> {
1289        check_vote::<T>(signed_vote, false)?;
1290
1291        ValidTransaction::with_tag_prefix("SubspaceVote")
1292            // We assign the maximum priority for any vote.
1293            .priority(TransactionPriority::MAX)
1294            // Should be included in the next block or block after that, but not later
1295            .longevity(2)
1296            .and_provides(signed_vote.signature)
1297            .build()
1298            .map(|validity| (validity, T::ExtensionWeightInfo::vote()))
1299    }
1300
1301    fn pre_dispatch_vote(
1302        signed_vote: &SignedVote<BlockNumberFor<T>, T::Hash, T::AccountId>,
1303    ) -> Result<Weight, TransactionValidityError> {
1304        match check_vote::<T>(signed_vote, true) {
1305            Ok(()) => Ok(T::ExtensionWeightInfo::vote()),
1306            Err(CheckVoteError::Equivocated { .. }) => {
1307                // Return Ok such that changes from this pre-dispatch are persisted
1308                Ok(T::ExtensionWeightInfo::vote_with_equivocation())
1309            }
1310            Err(error) => Err(error.into()),
1311        }
1312    }
1313}
1314
1315/// Verification data retrieval depends on whether it is called from pre_dispatch (meaning block
1316/// initialization has already happened) or from `validate_unsigned` by transaction pool (meaning
1317/// block initialization didn't happen yet).
1318fn current_vote_verification_data<T: Config>(is_block_initialized: bool) -> VoteVerificationData {
1319    let solution_ranges = SolutionRanges::<T>::get();
1320
1321    VoteVerificationData {
1322        solution_range: if is_block_initialized {
1323            solution_ranges.current
1324        } else {
1325            solution_ranges.next.unwrap_or(solution_ranges.current)
1326        },
1327        vote_solution_range: if is_block_initialized {
1328            solution_ranges.voting_current
1329        } else {
1330            solution_ranges
1331                .voting_next
1332                .unwrap_or(solution_ranges.voting_current)
1333        },
1334        current_slot: Pallet::<T>::current_slot(),
1335        parent_slot: ParentVoteVerificationData::<T>::get()
1336            .map(|parent_vote_verification_data| {
1337                if is_block_initialized {
1338                    parent_vote_verification_data.current_slot
1339                } else {
1340                    parent_vote_verification_data.parent_slot
1341                }
1342            })
1343            .unwrap_or_else(Pallet::<T>::current_slot),
1344    }
1345}
1346
1347#[derive(Debug, Eq, PartialEq)]
1348enum CheckVoteError {
1349    UnexpectedBeforeHeightTwo,
1350    HeightInTheFuture,
1351    HeightInThePast,
1352    IncorrectParentHash,
1353    SlotInTheFuture,
1354    SlotInThePast,
1355    BadRewardSignature(SignatureError),
1356    UnknownSegmentCommitment,
1357    InvalidHistorySize,
1358    InvalidSolution(String),
1359    QualityTooHigh,
1360    InvalidProofOfTime,
1361    InvalidFutureProofOfTime,
1362    DuplicateVote,
1363    Equivocated { slot: Slot, offender: PublicKey },
1364}
1365
1366impl From<CheckVoteError> for TransactionValidityError {
1367    #[inline]
1368    fn from(error: CheckVoteError) -> Self {
1369        TransactionValidityError::Invalid(match error {
1370            CheckVoteError::UnexpectedBeforeHeightTwo => InvalidTransaction::Call,
1371            CheckVoteError::HeightInTheFuture => InvalidTransaction::Future,
1372            CheckVoteError::HeightInThePast => InvalidTransaction::Stale,
1373            CheckVoteError::IncorrectParentHash => InvalidTransaction::Call,
1374            CheckVoteError::SlotInTheFuture => InvalidTransaction::Future,
1375            CheckVoteError::SlotInThePast => InvalidTransaction::Stale,
1376            CheckVoteError::BadRewardSignature(_) => InvalidTransaction::BadProof,
1377            CheckVoteError::UnknownSegmentCommitment => InvalidTransaction::Call,
1378            CheckVoteError::InvalidHistorySize => InvalidTransaction::Call,
1379            CheckVoteError::InvalidSolution(_) => InvalidTransaction::Call,
1380            CheckVoteError::QualityTooHigh => InvalidTransaction::Call,
1381            CheckVoteError::InvalidProofOfTime => InvalidTransaction::Future,
1382            CheckVoteError::InvalidFutureProofOfTime => InvalidTransaction::Call,
1383            CheckVoteError::DuplicateVote => InvalidTransaction::Call,
1384            CheckVoteError::Equivocated { .. } => InvalidTransaction::BadSigner,
1385        })
1386    }
1387}
1388
1389fn check_vote<T: Config>(
1390    signed_vote: &SignedVote<BlockNumberFor<T>, T::Hash, T::AccountId>,
1391    pre_dispatch: bool,
1392) -> Result<(), CheckVoteError> {
1393    let Vote::V0 {
1394        height,
1395        parent_hash,
1396        slot,
1397        solution,
1398        proof_of_time,
1399        future_proof_of_time,
1400    } = &signed_vote.vote;
1401    let height = *height;
1402    let slot = *slot;
1403
1404    let current_block_number = frame_system::Pallet::<T>::current_block_number();
1405
1406    if current_block_number <= One::one() || height <= One::one() {
1407        debug!("Votes are not expected at height below 2");
1408
1409        return Err(CheckVoteError::UnexpectedBeforeHeightTwo);
1410    }
1411
1412    // Height must be either the same as in current block or smaller by one.
1413    //
1414    // Subtraction will not panic due to check above.
1415    if !(height == current_block_number || height == current_block_number - One::one()) {
1416        debug!(
1417            "Vote verification error: bad height {height:?}, current block number is \
1418            {current_block_number:?}"
1419        );
1420        return Err(if height > current_block_number {
1421            CheckVoteError::HeightInTheFuture
1422        } else {
1423            CheckVoteError::HeightInThePast
1424        });
1425    }
1426
1427    // Should have parent hash from -1 (parent hash of current block) or -2 (block before that)
1428    //
1429    // Subtraction will not panic due to check above.
1430    if *parent_hash != frame_system::Pallet::<T>::block_hash(height - One::one()) {
1431        debug!("Vote verification error: parent hash {parent_hash:?}",);
1432        return Err(CheckVoteError::IncorrectParentHash);
1433    }
1434
1435    let current_vote_verification_data = current_vote_verification_data::<T>(pre_dispatch);
1436    let parent_vote_verification_data = ParentVoteVerificationData::<T>::get()
1437        .expect("Above check for block number ensures that this value is always present; qed");
1438
1439    if pre_dispatch {
1440        // New time slot is already set, whatever time slot is in the vote it must be smaller or the
1441        // same (for votes produced locally)
1442        let current_slot = current_vote_verification_data.current_slot;
1443        if slot > current_slot || (slot == current_slot && height != current_block_number) {
1444            debug!("Vote slot {slot:?} must be before current slot {current_slot:?}",);
1445            return Err(CheckVoteError::SlotInTheFuture);
1446        }
1447    }
1448
1449    let parent_slot = if pre_dispatch {
1450        // For pre-dispatch parent slot is `current_slot` in the parent vote verification data (it
1451        // was updated in current block because initialization hook was already called) if vote is
1452        // at the same height as the current block, otherwise it is one level older and
1453        // `parent_slot` from parent vote verification data needs to be taken instead
1454        if height == current_block_number {
1455            parent_vote_verification_data.current_slot
1456        } else {
1457            parent_vote_verification_data.parent_slot
1458        }
1459    } else {
1460        // Otherwise parent slot is `current_slot` in the current vote verification data (that
1461        // wasn't updated from parent block because initialization hook wasn't called yet) if vote
1462        // is at the same height as the current block, otherwise it is one level older and
1463        // `parent_slot` from current vote verification data needs to be taken instead
1464        if height == current_block_number {
1465            current_vote_verification_data.current_slot
1466        } else {
1467            current_vote_verification_data.parent_slot
1468        }
1469    };
1470
1471    if slot <= parent_slot {
1472        debug!("Vote slot {slot:?} must be after parent slot {parent_slot:?}",);
1473        return Err(CheckVoteError::SlotInThePast);
1474    }
1475
1476    if let Err(error) = check_reward_signature(
1477        signed_vote.vote.hash().as_bytes(),
1478        &signed_vote.signature,
1479        &solution.public_key,
1480        &schnorrkel::signing_context(REWARD_SIGNING_CONTEXT),
1481    ) {
1482        debug!("Vote verification error: {error:?}");
1483        return Err(CheckVoteError::BadRewardSignature(error));
1484    }
1485
1486    let vote_verification_data = if height == current_block_number {
1487        current_vote_verification_data
1488    } else {
1489        parent_vote_verification_data
1490    };
1491
1492    let sector_id = SectorId::new(
1493        solution.public_key.hash(),
1494        solution.sector_index,
1495        solution.history_size,
1496    );
1497
1498    let recent_segments = T::RecentSegments::get();
1499    let recent_history_fraction = (
1500        T::RecentHistoryFraction::get().0,
1501        T::RecentHistoryFraction::get().1,
1502    );
1503    let segment_index = sector_id
1504        .derive_piece_index(
1505            solution.piece_offset,
1506            solution.history_size,
1507            T::MaxPiecesInSector::get(),
1508            recent_segments,
1509            recent_history_fraction,
1510        )
1511        .segment_index();
1512
1513    let segment_commitment = if let Some(segment_commitment) =
1514        Pallet::<T>::segment_commitment(segment_index)
1515    {
1516        segment_commitment
1517    } else {
1518        debug!("Vote verification error: no segment commitment for segment index {segment_index}");
1519        return Err(CheckVoteError::UnknownSegmentCommitment);
1520    };
1521
1522    let sector_expiration_check_segment_commitment = Pallet::<T>::segment_commitment(
1523        solution
1524            .history_size
1525            .sector_expiration_check(T::MinSectorLifetime::get())
1526            .ok_or(CheckVoteError::InvalidHistorySize)?
1527            .segment_index(),
1528    );
1529
1530    match verify_solution(
1531        solution.into(),
1532        slot.into(),
1533        (&VerifySolutionParams {
1534            proof_of_time: *proof_of_time,
1535            solution_range: vote_verification_data.vote_solution_range,
1536            piece_check_params: Some(PieceCheckParams {
1537                max_pieces_in_sector: T::MaxPiecesInSector::get(),
1538                segment_commitment,
1539                recent_segments,
1540                recent_history_fraction,
1541                min_sector_lifetime: T::MinSectorLifetime::get(),
1542                current_history_size: Pallet::<T>::history_size(),
1543                sector_expiration_check_segment_commitment,
1544            }),
1545        })
1546            .into(),
1547    ) {
1548        Ok(solution_distance) => {
1549            if solution_distance <= vote_verification_data.solution_range / 2 {
1550                debug!("Vote quality is too high");
1551                return Err(CheckVoteError::QualityTooHigh);
1552            }
1553        }
1554        Err(error) => {
1555            debug!("Vote verification error: {error:?}");
1556            return Err(CheckVoteError::InvalidSolution(error));
1557        }
1558    }
1559
1560    // Cheap proof of time verification is possible here because proof of time must have already
1561    // been seen by this node due to votes requiring the same authoring delay as blocks
1562    if !is_proof_of_time_valid(
1563        BlockHash::try_from(parent_hash.as_ref())
1564            .expect("Must be able to convert to block hash type"),
1565        SlotNumber::from(slot),
1566        WrappedPotOutput::from(*proof_of_time),
1567        // Quick verification when entering transaction pool, but not when constructing the block
1568        !pre_dispatch,
1569    ) {
1570        debug!("Invalid proof of time");
1571
1572        return Err(CheckVoteError::InvalidProofOfTime);
1573    }
1574
1575    // During pre-dispatch we have already verified proofs of time up to future proof of time of
1576    // current block, which vote can't exceed, this must be possible to verify cheaply
1577    if pre_dispatch
1578        && !is_proof_of_time_valid(
1579            BlockHash::try_from(parent_hash.as_ref())
1580                .expect("Must be able to convert to block hash type"),
1581            SlotNumber::from(slot + T::BlockAuthoringDelay::get()),
1582            WrappedPotOutput::from(*future_proof_of_time),
1583            false,
1584        )
1585    {
1586        debug!("Invalid future proof of time");
1587
1588        return Err(CheckVoteError::InvalidFutureProofOfTime);
1589    }
1590
1591    let key = (
1592        solution.public_key,
1593        solution.sector_index,
1594        solution.piece_offset,
1595        solution.chunk,
1596        slot,
1597    );
1598    // Check that farmer didn't use solution from this vote yet in:
1599    // * parent block
1600    // * current block
1601    // * parent block vote
1602    // * current block vote
1603    let mut is_equivocating = ParentBlockAuthorInfo::<T>::get().as_ref() == Some(&key)
1604        || CurrentBlockAuthorInfo::<T>::get()
1605            .map(
1606                |(public_key, sector_index, piece_offset, chunk, slot, _reward_address)| {
1607                    (public_key, sector_index, piece_offset, chunk, slot)
1608                },
1609            )
1610            .as_ref()
1611            == Some(&key);
1612
1613    if !is_equivocating
1614        && let Some((_reward_address, signature)) = ParentBlockVoters::<T>::get().get(&key)
1615    {
1616        if signature != &signed_vote.signature {
1617            is_equivocating = true;
1618        } else {
1619            // The same vote should never be included more than once
1620            return Err(CheckVoteError::DuplicateVote);
1621        }
1622    }
1623
1624    if !is_equivocating
1625        && let Some((_reward_address, signature)) =
1626            CurrentBlockVoters::<T>::get().unwrap_or_default().get(&key)
1627    {
1628        if signature != &signed_vote.signature {
1629            is_equivocating = true;
1630        } else {
1631            // The same vote should never be included more than once
1632            return Err(CheckVoteError::DuplicateVote);
1633        }
1634    }
1635
1636    if pre_dispatch {
1637        // During `pre_dispatch` call put farmer into the list of reward receivers.
1638        CurrentBlockVoters::<T>::mutate(|current_reward_receivers| {
1639            current_reward_receivers
1640                .as_mut()
1641                .expect("Always set during block initialization")
1642                .insert(
1643                    key,
1644                    (
1645                        if is_equivocating {
1646                            None
1647                        } else {
1648                            Some(solution.reward_address.clone())
1649                        },
1650                        signed_vote.signature,
1651                    ),
1652                );
1653        });
1654    }
1655
1656    if is_equivocating {
1657        let offender = solution.public_key;
1658
1659        CurrentBlockAuthorInfo::<T>::mutate(|maybe_info| {
1660            if let Some((public_key, _sector_index, _piece_offset, _chunk, _slot, reward_address)) =
1661                maybe_info
1662                && public_key == &offender
1663            {
1664                // Revoke reward for block author
1665                reward_address.take();
1666            }
1667        });
1668
1669        CurrentBlockVoters::<T>::mutate(|current_reward_receivers| {
1670            if let Some(current_reward_receivers) = current_reward_receivers {
1671                for (
1672                    (public_key, _sector_index, _piece_offset, _chunk, _slot),
1673                    (reward_address, _signature),
1674                ) in current_reward_receivers.iter_mut()
1675                {
1676                    if public_key == &offender {
1677                        // Revoke reward if assigned in current block.
1678                        reward_address.take();
1679                    }
1680                }
1681            }
1682        });
1683
1684        return Err(CheckVoteError::Equivocated { slot, offender });
1685    }
1686
1687    Ok(())
1688}
1689
1690fn check_segment_headers<T: Config>(
1691    segment_headers: &[SegmentHeader],
1692) -> Result<(), TransactionValidityError> {
1693    let mut segment_headers_iter = segment_headers.iter();
1694
1695    // There should be some segment headers
1696    let first_segment_header = match segment_headers_iter.next() {
1697        Some(first_segment_header) => first_segment_header,
1698        None => {
1699            return Err(InvalidTransaction::BadMandatory.into());
1700        }
1701    };
1702
1703    // Segment in segment headers should monotonically increase
1704    if first_segment_header.segment_index() > SegmentIndex::ZERO
1705        && !SegmentCommitment::<T>::contains_key(
1706            first_segment_header.segment_index() - SegmentIndex::ONE,
1707        )
1708    {
1709        return Err(InvalidTransaction::BadMandatory.into());
1710    }
1711
1712    // Segment headers should never repeat
1713    if SegmentCommitment::<T>::contains_key(first_segment_header.segment_index()) {
1714        return Err(InvalidTransaction::BadMandatory.into());
1715    }
1716
1717    let mut last_segment_index = first_segment_header.segment_index();
1718
1719    for segment_header in segment_headers_iter {
1720        let segment_index = segment_header.segment_index();
1721
1722        // Segment in segment headers should monotonically increase
1723        if segment_index != last_segment_index + SegmentIndex::ONE {
1724            return Err(InvalidTransaction::BadMandatory.into());
1725        }
1726
1727        // Segment headers should never repeat
1728        if SegmentCommitment::<T>::contains_key(segment_index) {
1729            return Err(InvalidTransaction::BadMandatory.into());
1730        }
1731
1732        last_segment_index = segment_index;
1733    }
1734
1735    Ok(())
1736}
1737
1738impl<T: Config> subspace_runtime_primitives::RewardsEnabled for Pallet<T> {
1739    fn rewards_enabled() -> bool {
1740        if let Some(height) = EnableRewards::<T>::get() {
1741            frame_system::Pallet::<T>::current_block_number() >= height
1742        } else {
1743            false
1744        }
1745    }
1746}
1747
1748impl<T: Config> subspace_runtime_primitives::FindBlockRewardAddress<T::AccountId> for Pallet<T> {
1749    fn find_block_reward_address() -> Option<T::AccountId> {
1750        CurrentBlockAuthorInfo::<T>::get().and_then(
1751            |(_public_key, _sector_index, _piece_offset, _chunk, _slot, reward_address)| {
1752                // Rewards might be disabled, in which case no block reward
1753                if let Some(height) = EnableRewards::<T>::get()
1754                    && frame_system::Pallet::<T>::current_block_number() >= height
1755                {
1756                    return reward_address;
1757                }
1758
1759                None
1760            },
1761        )
1762    }
1763}
1764
1765impl<T: Config> subspace_runtime_primitives::FindVotingRewardAddresses<T::AccountId> for Pallet<T> {
1766    fn find_voting_reward_addresses() -> Vec<T::AccountId> {
1767        // Rewards might be disabled, in which case no voting reward
1768        if let Some(height) = EnableRewards::<T>::get()
1769            && frame_system::Pallet::<T>::current_block_number() >= height
1770        {
1771            // It is possible that this is called during initialization when current block
1772            // voters are already moved into parent block voters, handle it accordingly
1773            return CurrentBlockVoters::<T>::get()
1774                .unwrap_or_else(ParentBlockVoters::<T>::get)
1775                .into_values()
1776                .filter_map(|(reward_address, _signature)| reward_address)
1777                .collect();
1778        }
1779
1780        Vec::new()
1781    }
1782}
1783
1784impl<T: Config> frame_support::traits::Randomness<T::Hash, BlockNumberFor<T>> for Pallet<T> {
1785    fn random(subject: &[u8]) -> (T::Hash, BlockNumberFor<T>) {
1786        let mut subject = subject.to_vec();
1787        subject.extend_from_slice(
1788            BlockRandomness::<T>::get()
1789                .expect("Block randomness is always set in block initialization; qed")
1790                .as_ref(),
1791        );
1792
1793        (
1794            T::Hashing::hash(&subject),
1795            frame_system::Pallet::<T>::current_block_number(),
1796        )
1797    }
1798
1799    fn random_seed() -> (T::Hash, BlockNumberFor<T>) {
1800        (
1801            T::Hashing::hash(
1802                BlockRandomness::<T>::get()
1803                    .expect("Block randomness is always set in block initialization; qed")
1804                    .as_ref(),
1805            ),
1806            frame_system::Pallet::<T>::current_block_number(),
1807        )
1808    }
1809}