From ddf6b2128e89b7d005f091e44ba708fad2a955bb Mon Sep 17 00:00:00 2001 From: Nazar Mokrynskyi Date: Mon, 28 Aug 2023 20:50:19 +0300 Subject: [PATCH] Introduce notion of iterations for proof of time into `pallet-subspace` and tune it slightly lower for local/devnet for better dev experience --- Cargo.toml | 2 + crates/pallet-subspace/src/lib.rs | 37 ++++++++++++-- crates/pallet-subspace/src/mock.rs | 13 +++-- .../sc-consensus-subspace/src/slot_worker.rs | 8 ++- crates/sc-proof-of-time/src/lib.rs | 4 -- crates/sc-proof-of-time/src/source.rs | 49 +++++++++++++++---- crates/sp-consensus-subspace/src/lib.rs | 5 ++ crates/subspace-node/src/chain_spec.rs | 9 ++++ crates/subspace-runtime/src/lib.rs | 22 ++++++--- crates/subspace-service/src/lib.rs | 4 +- test/subspace-test-client/src/chain_spec.rs | 2 + test/subspace-test-runtime/src/lib.rs | 22 ++++++--- 12 files changed, 132 insertions(+), 45 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 16ac35c337e..b1f89fb27c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,12 +64,14 @@ sha2 = { opt-level = 3 } sha3 = { opt-level = 3 } smallvec = { opt-level = 3 } snow = { opt-level = 3 } +sc-proof-of-time = { opt-level = 3 } subspace-archiving = { opt-level = 3 } subspace-chiapos = { opt-level = 3 } subspace-core-primitives = { opt-level = 3 } subspace-erasure-coding = { opt-level = 3 } subspace-farmer-components = { opt-level = 3 } subspace-proof-of-space = { opt-level = 3 } +subspace-proof-of-time = { opt-level = 3 } twox-hash = { opt-level = 3 } uint = { opt-level = 3 } x25519-dalek = { opt-level = 3 } diff --git a/crates/pallet-subspace/src/lib.rs b/crates/pallet-subspace/src/lib.rs index db3784399a1..2d0b92a3420 100644 --- a/crates/pallet-subspace/src/lib.rs +++ b/crates/pallet-subspace/src/lib.rs @@ -65,6 +65,7 @@ use sp_runtime::transaction_validity::{ }; use sp_runtime::DispatchError; use sp_std::collections::btree_map::BTreeMap; +use sp_std::num::NonZeroU32; use sp_std::prelude::*; use subspace_core_primitives::crypto::Scalar; #[cfg(feature = "pot")] @@ -148,6 +149,7 @@ mod pallet { use sp_consensus_subspace::{EquivocationProof, FarmerPublicKey, FarmerSignature, SignedVote}; use sp_runtime::DigestItem; use sp_std::collections::btree_map::BTreeMap; + use sp_std::num::NonZeroU32; use sp_std::prelude::*; use subspace_core_primitives::crypto::Scalar; use subspace_core_primitives::{ @@ -299,16 +301,16 @@ mod pallet { pub enable_storage_access: bool, /// Who can author blocks at genesis. pub allow_authoring_by: AllowAuthoringBy, + /// Number of iterations for proof of time per slot + pub pot_slot_iterations: NonZeroU32, } impl Default for GenesisConfig { #[inline] fn default() -> Self { - Self { - enable_rewards: true, - enable_storage_access: true, - allow_authoring_by: AllowAuthoringBy::Anyone, - } + // TODO: Remove once https://github.com/paritytech/polkadot-sdk/pull/1221 is in our + // fork + unreachable!("Config must be initialized explicitly"); } } @@ -331,6 +333,7 @@ mod pallet { RootPlotPublicKey::::put(root_farmer.clone()); } } + PotSlotIterations::::put(self.pot_slot_iterations.get()); } } @@ -376,6 +379,22 @@ mod pallet { pub(super) type GlobalRandomnesses = StorageValue<_, sp_consensus_subspace::GlobalRandomnesses, ValueQuery>; + pub(super) struct DefaultPotSlotIterations {} + + // TODO: Replace with `NonZeroU32` once we can use it: + // https://github.com/paritytech/parity-scale-codec/pull/505 + impl Get for DefaultPotSlotIterations { + fn get() -> u32 { + unreachable!("Always instantiated during genesis; qed"); + } + } + + /// Number of iterations for proof of time per slot + #[pallet::storage] + // #[pallet::getter(fn pot_slot_iterations)] + pub(super) type PotSlotIterations = + StorageValue<_, u32, ValueQuery, DefaultPotSlotIterations>; + /// Solution ranges used for challenges. #[pallet::storage] #[pallet::getter(fn solution_ranges)] @@ -1064,6 +1083,14 @@ impl Pallet { Some(()) } + /// Number of iterations for proof of time per slot + // TODO: Remove once we can use `NonZeroU32` directly: + // https://github.com/paritytech/parity-scale-codec/pull/505 + pub fn pot_slot_iterations() -> NonZeroU32 { + NonZeroU32::new(PotSlotIterations::::get()) + .expect("Always initialized to non-zero value; qed") + } + /// Check if `farmer_public_key` is in block list (due to equivocation) pub fn is_in_block_list(farmer_public_key: &FarmerPublicKey) -> bool { BlockList::::contains_key(farmer_public_key) diff --git a/crates/pallet-subspace/src/mock.rs b/crates/pallet-subspace/src/mock.rs index bdd477f9fe4..facd4a57fbf 100644 --- a/crates/pallet-subspace/src/mock.rs +++ b/crates/pallet-subspace/src/mock.rs @@ -18,8 +18,8 @@ use crate::equivocation::EquivocationHandler; use crate::{ - self as pallet_subspace, Config, CurrentSlot, FarmerPublicKey, NormalEraChange, - NormalGlobalRandomnessInterval, + self as pallet_subspace, AllowAuthoringBy, Config, CurrentSlot, FarmerPublicKey, + NormalEraChange, NormalGlobalRandomnessInterval, }; use frame_support::pallet_prelude::Weight; use frame_support::parameter_types; @@ -39,7 +39,7 @@ use sp_runtime::testing::{Digest, DigestItem, Header, TestXt}; use sp_runtime::traits::{Block as BlockT, Header as _, IdentityLookup}; use sp_runtime::Perbill; use std::iter; -use std::num::NonZeroU64; +use std::num::{NonZeroU32, NonZeroU64}; use std::sync::Once; use subspace_archiving::archiver::{Archiver, NewArchivedSegment}; use subspace_core_primitives::crypto::kzg::{embedded_kzg_settings, Kzg}; @@ -286,7 +286,12 @@ pub fn new_test_ext() -> TestExternalities { .unwrap(); GenesisBuild::::assimilate_storage( - &pallet_subspace::GenesisConfig::default(), + &pallet_subspace::GenesisConfig { + enable_rewards: true, + enable_storage_access: true, + allow_authoring_by: AllowAuthoringBy::Anyone, + pot_slot_iterations: NonZeroU32::new(100_000).unwrap(), + }, &mut storage, ) .unwrap(); diff --git a/crates/sc-consensus-subspace/src/slot_worker.rs b/crates/sc-consensus-subspace/src/slot_worker.rs index ede77555399..46bcb1d4831 100644 --- a/crates/sc-consensus-subspace/src/slot_worker.rs +++ b/crates/sc-consensus-subspace/src/slot_worker.rs @@ -54,8 +54,6 @@ use sp_runtime::DigestItem; use std::collections::BTreeMap; use std::future::Future; use std::marker::PhantomData; -#[cfg(feature = "pot")] -use std::num::NonZeroU32; use std::pin::Pin; use std::sync::Arc; #[cfg(feature = "pot")] @@ -299,9 +297,10 @@ where let (solution_range, voting_solution_range) = extract_solution_ranges_for_block(self.client.as_ref(), parent_hash).ok()?; + #[cfg(feature = "pot")] + let pot_slot_iterations = runtime_api.pot_slot_iterations(parent_hash).ok()?; let maybe_root_plot_public_key = runtime_api.root_plot_public_key(parent_hash).ok()?; - // TODO: Store `new_checkpoints` #[cfg(feature = "pot")] let (proof_of_time, future_proof_of_time, new_checkpoints) = { let mut pot_checkpoints = self.pot_checkpoints.lock(); @@ -473,8 +472,7 @@ where solution, #[cfg(feature = "pot")] pot_info: PreDigestPotInfo::Regular { - // TODO: Replace with correct value from runtime state - iterations: NonZeroU32::MIN, + iterations: pot_slot_iterations, proof_of_time, future_proof_of_time, }, diff --git a/crates/sc-proof-of-time/src/lib.rs b/crates/sc-proof-of-time/src/lib.rs index bf40501f87e..0780983c196 100644 --- a/crates/sc-proof-of-time/src/lib.rs +++ b/crates/sc-proof-of-time/src/lib.rs @@ -107,10 +107,6 @@ pub async fn start_slot_worker( if let Some(slot_info) = slot_info_producer.produce_slot_info(slot_to_claim).await { let _ = worker.on_slot(slot_info).await; - - // TODO: Remove this hack, it restricts slot production with extremely low number of - // iterations - tokio::time::sleep(std::time::Duration::from_secs(1)).await; } } } diff --git a/crates/sc-proof-of-time/src/source.rs b/crates/sc-proof-of-time/src/source.rs index 135fa9a183d..f604516b99b 100644 --- a/crates/sc-proof-of-time/src/source.rs +++ b/crates/sc-proof-of-time/src/source.rs @@ -2,8 +2,14 @@ use derive_more::{Deref, DerefMut, From}; use futures::channel::mpsc; use futures::executor::block_on; use futures::SinkExt; +use sp_api::{ApiError, ProvideRuntimeApi}; +use sp_blockchain::HeaderBackend; use sp_consensus_slots::Slot; +use sp_consensus_subspace::{FarmerPublicKey, SubspaceApi as SubspaceRuntimeApi}; +use sp_runtime::traits::Block as BlockT; +use std::marker::PhantomData; use std::num::NonZeroU32; +use std::sync::Arc; use std::thread; use subspace_core_primitives::{PotCheckpoints, PotKey, PotSeed, SlotNumber}; use subspace_proof_of_time::PotError; @@ -35,18 +41,40 @@ pub struct PotSourceConfig { /// Depending on configuration may produce proofs of time locally, send/receive via gossip and keep /// up to day with blockchain reorgs. #[derive(Debug)] -pub struct PotSource { - // TODO +pub struct PotSource { + // TODO: Use this in `fn run` + #[allow(dead_code)] + client: Arc, + _block: PhantomData, } -impl PotSource { - pub fn new(config: PotSourceConfig) -> (Self, PotSlotInfoStream) { +impl PotSource +where + Block: BlockT, + Client: ProvideRuntimeApi + HeaderBackend, + Client::Api: SubspaceRuntimeApi, +{ + pub fn new( + config: PotSourceConfig, + client: Arc, + ) -> Result<(Self, PotSlotInfoStream), ApiError> { + let PotSourceConfig { + // TODO: Respect this boolean flag + is_timekeeper: _, + initial_key, + } = config; // TODO: All 3 are incorrect and should be able to continue after node restart let start_slot = SlotNumber::MIN; let start_seed = PotSeed::default(); - let start_key = config.initial_key; - // TODO: Change to correct values taken from blockchain - let iterations = NonZeroU32::new(1024).expect("Not zero; qed"); + let start_key = initial_key; + #[cfg(feature = "pot")] + let best_hash = client.info().best_hash; + #[cfg(feature = "pot")] + let runtime_api = client.runtime_api(); + #[cfg(feature = "pot")] + let iterations = runtime_api.pot_slot_iterations(best_hash)?; + #[cfg(not(feature = "pot"))] + let iterations = NonZeroU32::new(100_000_000).expect("Not zero; qed"); // TODO: Correct capacity let (slot_sender, slot_receiver) = mpsc::channel(10); @@ -61,12 +89,13 @@ impl PotSource { }) .expect("Thread creation must not panic"); - ( + Ok(( Self { - // TODO + client, + _block: PhantomData, }, PotSlotInfoStream(slot_receiver), - ) + )) } /// Run proof of time source diff --git a/crates/sp-consensus-subspace/src/lib.rs b/crates/sp-consensus-subspace/src/lib.rs index 7049af5d49b..2c09cbbaf04 100644 --- a/crates/sp-consensus-subspace/src/lib.rs +++ b/crates/sp-consensus-subspace/src/lib.rs @@ -41,6 +41,8 @@ use sp_io::hashing; use sp_runtime::{ConsensusEngineId, DigestItem, Justification}; use sp_runtime_interface::pass_by::PassBy; use sp_runtime_interface::{pass_by, runtime_interface}; +#[cfg(feature = "pot")] +use sp_std::num::NonZeroU32; use sp_std::vec::Vec; use subspace_core_primitives::crypto::kzg::Kzg; #[cfg(not(feature = "pot"))] @@ -621,6 +623,9 @@ sp_api::decl_runtime_apis! { /// The slot duration in milliseconds for Subspace. fn slot_duration() -> SlotDuration; + /// Number of iterations for proof of time per slot + fn pot_slot_iterations() -> NonZeroU32; + /// Solution ranges. fn solution_ranges() -> SolutionRanges; diff --git a/crates/subspace-node/src/chain_spec.rs b/crates/subspace-node/src/chain_spec.rs index 0a979acb2a6..944c53c6e3b 100644 --- a/crates/subspace-node/src/chain_spec.rs +++ b/crates/subspace-node/src/chain_spec.rs @@ -25,6 +25,7 @@ use sp_consensus_subspace::FarmerPublicKey; use sp_core::crypto::{Ss58Codec, UncheckedFrom}; use sp_domains::RuntimeType; use sp_runtime::Percent; +use std::num::NonZeroU32; use subspace_core_primitives::PotKey; use subspace_runtime::{ AllowAuthoringBy, BalancesConfig, DomainsConfig, GenesisConfig, MaxDomainBlockSize, @@ -81,6 +82,7 @@ struct GenesisParams { enable_rewards: bool, enable_storage_access: bool, allow_authoring_by: AllowAuthoringBy, + pot_slot_iterations: NonZeroU32, enable_domains: bool, enable_transfer: bool, confirmation_depth_k: u32, @@ -149,6 +151,8 @@ pub fn gemini_3f_compiled() -> Result, String> "8aecbcf0b404590ddddc01ebacb205a562d12fdb5c2aa6a4035c1a20f23c9515" )), ), + // TODO: Adjust once we bench PoT on faster hardware + pot_slot_iterations: NonZeroU32::new(183_270_000).expect("Not zero; qed"), enable_domains: true, enable_transfer: false, confirmation_depth_k: 100, // TODO: Proper value here @@ -246,6 +250,7 @@ pub fn devnet_config_compiled() -> Result, Str enable_rewards: false, enable_storage_access: false, allow_authoring_by: AllowAuthoringBy::FirstFarmer, + pot_slot_iterations: NonZeroU32::new(100_000_000).expect("Not zero; qed"), enable_domains: true, enable_transfer: true, confirmation_depth_k: 100, // TODO: Proper value here @@ -303,6 +308,7 @@ pub fn dev_config() -> Result, String> { enable_rewards: false, enable_storage_access: false, allow_authoring_by: AllowAuthoringBy::Anyone, + pot_slot_iterations: NonZeroU32::new(150_000_000).expect("Not zero; qed"), enable_domains: true, enable_transfer: true, confirmation_depth_k: 5, @@ -365,6 +371,7 @@ pub fn local_config() -> Result, String> { enable_rewards: false, enable_storage_access: false, allow_authoring_by: AllowAuthoringBy::Anyone, + pot_slot_iterations: NonZeroU32::new(100_000_000).expect("Not zero; qed"), enable_domains: true, enable_transfer: true, confirmation_depth_k: 1, @@ -406,6 +413,7 @@ fn subspace_genesis_config( enable_rewards, enable_storage_access, allow_authoring_by, + pot_slot_iterations, enable_domains, enable_transfer, confirmation_depth_k, @@ -433,6 +441,7 @@ fn subspace_genesis_config( enable_rewards, enable_storage_access, allow_authoring_by, + pot_slot_iterations, }, vesting: VestingConfig { vesting }, runtime_configs: RuntimeConfigsConfig { diff --git a/crates/subspace-runtime/src/lib.rs b/crates/subspace-runtime/src/lib.rs index b415e2dc1f1..e2bb4ce7d1c 100644 --- a/crates/subspace-runtime/src/lib.rs +++ b/crates/subspace-runtime/src/lib.rs @@ -70,6 +70,8 @@ use sp_runtime::transaction_validity::{TransactionSource, TransactionValidity}; use sp_runtime::{ create_runtime_str, generic, AccountId32, ApplyExtrinsicResult, Perbill, SaturatedConversion, }; +#[cfg(feature = "pot")] +use sp_std::num::NonZeroU32; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -1093,18 +1095,14 @@ impl_runtime_apis! { } impl sp_consensus_subspace::SubspaceApi for Runtime { - fn history_size() -> HistorySize { - >::history_size() - } - - fn max_pieces_in_sector() -> u16 { - MAX_PIECES_IN_SECTOR - } - fn slot_duration() -> SlotDuration { SlotDuration::from_millis(SLOT_DURATION) } + fn pot_slot_iterations() -> NonZeroU32 { + Subspace::pot_slot_iterations() + } + fn solution_ranges() -> SolutionRanges { Subspace::solution_ranges() } @@ -1143,6 +1141,14 @@ impl_runtime_apis! { Subspace::is_in_block_list(farmer_public_key) } + fn history_size() -> HistorySize { + >::history_size() + } + + fn max_pieces_in_sector() -> u16 { + MAX_PIECES_IN_SECTOR + } + fn segment_commitment(segment_index: SegmentIndex) -> Option { Subspace::segment_commitment(segment_index) } diff --git a/crates/subspace-service/src/lib.rs b/crates/subspace-service/src/lib.rs index 477a4100b2f..0d481e8a812 100644 --- a/crates/subspace-service/src/lib.rs +++ b/crates/subspace-service/src/lib.rs @@ -764,7 +764,9 @@ where if config.role.is_authority() || config.force_new_slot_notifications { #[cfg(feature = "pot")] - let (pot_source, pot_slot_info_stream) = PotSource::new(config.pot_source_config); + let (pot_source, pot_slot_info_stream) = + PotSource::new(config.pot_source_config, client.clone()) + .map_err(|error| Error::Other(error.into()))?; #[cfg(feature = "pot")] { task_manager.spawn_essential_handle().spawn_blocking( diff --git a/test/subspace-test-client/src/chain_spec.rs b/test/subspace-test-client/src/chain_spec.rs index 0bed5402dd2..103685afbea 100644 --- a/test/subspace-test-client/src/chain_spec.rs +++ b/test/subspace-test-client/src/chain_spec.rs @@ -6,6 +6,7 @@ use sp_core::{sr25519, Pair, Public}; use sp_domains::{GenesisDomain, OperatorPublicKey, RuntimeType}; use sp_runtime::traits::{IdentifyAccount, Verify}; use sp_runtime::Percent; +use std::num::NonZeroU32; use subspace_runtime_primitives::{AccountId, Balance, BlockNumber, Signature}; use subspace_test_runtime::{ AllowAuthoringBy, BalancesConfig, DomainsConfig, GenesisConfig, MaxDomainBlockSize, @@ -99,6 +100,7 @@ fn create_genesis_config( enable_rewards: false, enable_storage_access: false, allow_authoring_by: AllowAuthoringBy::Anyone, + pot_slot_iterations: NonZeroU32::new(50_000_000).expect("Not zero; qed"), }, vesting: VestingConfig { vesting }, domains: DomainsConfig { diff --git a/test/subspace-test-runtime/src/lib.rs b/test/subspace-test-runtime/src/lib.rs index a90d36a6018..26ed029846e 100644 --- a/test/subspace-test-runtime/src/lib.rs +++ b/test/subspace-test-runtime/src/lib.rs @@ -69,6 +69,8 @@ use sp_runtime::{ }; use sp_std::iter::Peekable; use sp_std::marker::PhantomData; +#[cfg(feature = "pot")] +use sp_std::num::NonZeroU32; use sp_std::prelude::*; #[cfg(feature = "std")] use sp_version::NativeVersion; @@ -1403,18 +1405,14 @@ impl_runtime_apis! { } impl sp_consensus_subspace::SubspaceApi for Runtime { - fn history_size() -> HistorySize { - >::history_size() - } - - fn max_pieces_in_sector() -> u16 { - MAX_PIECES_IN_SECTOR - } - fn slot_duration() -> SlotDuration { SlotDuration::from_millis(SLOT_DURATION) } + fn pot_slot_iterations() -> NonZeroU32 { + Subspace::pot_slot_iterations() + } + fn solution_ranges() -> SolutionRanges { Subspace::solution_ranges() } @@ -1453,6 +1451,14 @@ impl_runtime_apis! { Subspace::is_in_block_list(farmer_public_key) } + fn history_size() -> HistorySize { + >::history_size() + } + + fn max_pieces_in_sector() -> u16 { + MAX_PIECES_IN_SECTOR + } + fn segment_commitment(segment_index: SegmentIndex) -> Option { Subspace::segment_commitment(segment_index) }