Skip to content
This repository has been archived by the owner on Feb 21, 2024. It is now read-only.

Commit

Permalink
Merge pull request paritytech#374 from subspace/change/solution_adjus…
Browse files Browse the repository at this point in the history
…tment
  • Loading branch information
vedhavyas authored Apr 18, 2022
2 parents 9d200ea + 49518f0 commit e7a8a33
Show file tree
Hide file tree
Showing 9 changed files with 116 additions and 30 deletions.
2 changes: 1 addition & 1 deletion crates/pallet-subspace/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ subspace-solving = { version = "0.1.0", path = "../subspace-solving" }

[features]
default = ["std"]
no-early-solution-range-updates = []
std = [
"codec/std",
"frame-support/std",
Expand All @@ -52,5 +51,6 @@ std = [
"sp-runtime/std",
"sp-std/std",
"subspace-core-primitives/std",
"subspace-runtime-primitives/std"
]
try-runtime = ["frame-support/try-runtime"]
67 changes: 40 additions & 27 deletions crates/pallet-subspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ pub struct NormalEraChange;
impl EraChangeTrigger for NormalEraChange {
fn trigger<T: Config>(block_number: T::BlockNumber) {
if <Pallet<T>>::should_era_change(block_number) {
<Pallet<T>>::enact_era_change(block_number);
<Pallet<T>>::enact_era_change();
}
}
}
Expand Down Expand Up @@ -218,6 +218,8 @@ mod pallet {
#[pallet::constant]
type RecordedHistorySegmentSize: Get<u32>;

type ShouldAdjustSolutionRange: Get<bool>;

/// Subspace requires periodic global randomness update.
type GlobalRandomnessIntervalTrigger: GlobalRandomnessIntervalTrigger;

Expand Down Expand Up @@ -292,6 +294,11 @@ mod pallet {
InitialSolutionRanges<T>,
>;

/// Storage to check if the solution range is to be adjusted for next era
#[pallet::storage]
pub type ShouldAdjustSolutionRange<T: Config> =
StorageValue<_, bool, ValueQuery, T::ShouldAdjustSolutionRange>;

/// Salts used for challenges.
#[pallet::storage]
#[pallet::getter(fn salts)]
Expand Down Expand Up @@ -357,6 +364,15 @@ mod pallet {
ensure_none(origin)?;
Self::do_store_root_blocks(root_blocks)
}

/// Enables solution range adjustment after every era.
/// Note: No effect on the solution range for the current era
#[pallet::weight(T::DbWeight::get().writes(1))]
pub fn enable_solution_range_adjustment(origin: OriginFor<T>) -> DispatchResult {
ensure_root(origin)?;
ShouldAdjustSolutionRange::<T>::put(true);
Ok(())
}
}

#[pallet::inherent]
Expand Down Expand Up @@ -484,40 +500,37 @@ impl<T: Config> Pallet<T> {
/// returned `true`, and the caller is the only caller of this function.
///
/// This will update solution range used in consensus.
pub fn enact_era_change(block_number: T::BlockNumber) {
pub fn enact_era_change() {
let slot_probability = T::SlotProbability::get();

let current_slot = Self::current_slot();

SolutionRanges::<T>::mutate(|solution_ranges| {
// If Era start slot is not found it means we have just finished the first era
let era_start_slot = EraStartSlot::<T>::get().unwrap_or_else(GenesisSlot::<T>::get);
let era_slot_count = u64::from(current_slot) - u64::from(era_start_slot);

// Now we need to re-calculate solution range. The idea here is to keep block production at
// the same pace while space pledged on the network changes. For this we adjust previous
// solution range according to actual and expected number of blocks per era.
let era_duration: u64 = T::EraDuration::get()
.try_into()
.unwrap_or_else(|_| panic!("Era duration is always within u64; qed"));
let actual_slots_per_block = era_slot_count as f64 / era_duration as f64;
let expected_slots_per_block = slot_probability.1 as f64 / slot_probability.0 as f64;
let adjustment_factor =
(actual_slots_per_block / expected_slots_per_block).clamp(0.25, 4.0);

solution_ranges.next.replace(
// TODO: Temporary testnet hack, we don't update solution range for the first 15_000 blocks
// in order to seed the blockchain with data quickly
if cfg!(all(feature = "no-early-solution-range-updates", not(test))) {
if block_number < 15_000_u32.into() {
solution_ranges.current
} else {
(solution_ranges.current as f64 * adjustment_factor).round() as u64
}
} else {
// Check if the solution range should be adjusted for next era.
if ShouldAdjustSolutionRange::<T>::get() {
// If Era start slot is not found it means we have just finished the first era
let era_start_slot =
EraStartSlot::<T>::get().unwrap_or_else(GenesisSlot::<T>::get);
let era_slot_count = u64::from(current_slot) - u64::from(era_start_slot);

// Now we need to re-calculate solution range. The idea here is to keep block production at
// the same pace while space pledged on the network changes. For this we adjust previous
// solution range according to actual and expected number of blocks per era.
let era_duration: u64 = T::EraDuration::get()
.try_into()
.unwrap_or_else(|_| panic!("Era duration is always within u64; qed"));
let actual_slots_per_block = era_slot_count as f64 / era_duration as f64;
let expected_slots_per_block =
slot_probability.1 as f64 / slot_probability.0 as f64;
let adjustment_factor =
(actual_slots_per_block / expected_slots_per_block).clamp(0.25, 4.0);

(solution_ranges.current as f64 * adjustment_factor).round() as u64
} else {
solution_ranges.current
},
);
)
});

EraStartSlot::<T>::put(current_slot);
Expand Down
2 changes: 2 additions & 0 deletions crates/pallet-subspace/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ parameter_types! {
pub const ReplicationFactor: u16 = 1;
pub const ReportLongevity: u64 = 34;
pub const MaxPlotSize: u64 = 10 * 2u64.pow(18);
pub const ShouldAdjustSolutionRange: bool = false;
}

impl Config for Test {
Expand All @@ -164,6 +165,7 @@ impl Config for Test {
type HandleEquivocation = EquivocationHandler<OffencesSubspace, ReportLongevity>;

type WeightInfo = ();
type ShouldAdjustSolutionRange = ShouldAdjustSolutionRange;
}

pub fn go_to_block(keypair: &Keypair, block: u64, slot: u64) {
Expand Down
63 changes: 63 additions & 0 deletions crates/pallet-subspace/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ fn can_update_solution_range_on_era_change() {
next: None,
};
assert_eq!(Subspace::solution_ranges(), initial_solution_ranges);
// enable solution range adjustment
assert_ok!(Subspace::enable_solution_range_adjustment(Origin::root()));

// Progress to almost era edge
progress_to_block(&keypair, 3);
Expand Down Expand Up @@ -142,6 +144,67 @@ fn can_update_solution_range_on_era_change() {
})
}

#[test]
fn solution_range_should_not_update_when_disabled() {
new_test_ext().execute_with(|| {
let keypair = Keypair::generate();

assert_eq!(<Test as Config>::EraDuration::get(), 4);
assert_eq!(
<Test as Config>::InitialSolutionRange::get(),
INITIAL_SOLUTION_RANGE
);
let initial_solution_ranges = SolutionRanges {
current: INITIAL_SOLUTION_RANGE,
next: None,
};
assert_eq!(Subspace::solution_ranges(), initial_solution_ranges);

// Progress to almost era edge
progress_to_block(&keypair, 3);
// No solution range update
assert_eq!(Subspace::solution_ranges(), initial_solution_ranges);

// Era edge
progress_to_block(&keypair, 4);
// Next solution range should be updated, but current is still unchanged
let updated_solution_ranges = Subspace::solution_ranges();
assert_eq!(
updated_solution_ranges.current,
initial_solution_ranges.current
);
assert!(updated_solution_ranges.next.is_some());

progress_to_block(&keypair, 5);
// Next solution range should become current
assert_eq!(
Subspace::solution_ranges(),
SolutionRanges {
current: updated_solution_ranges.next.unwrap(),
next: None
}
);

// since solution range adjustment was disabled, solution range will remain the same
let last_solution_range = Subspace::solution_ranges().current;
assert_eq!(last_solution_range, INITIAL_SOLUTION_RANGE);

// Progress to era edge such that it takes more slots than expected
go_to_block(
&keypair,
8,
u64::from(Subspace::current_slot())
+ (4 * SLOT_PROBABILITY.1 / SLOT_PROBABILITY.0 + 10),
);
// Solution rage will still be the same even after the apparent pledged space has decreased
// since adjustment is disabled
assert_eq!(
Subspace::solution_ranges().next.unwrap(),
INITIAL_SOLUTION_RANGE
);
})
}

#[test]
fn can_update_salt_on_eon_change() {
new_test_ext().execute_with(|| {
Expand Down
2 changes: 1 addition & 1 deletion crates/subspace-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pallet-grandpa-finality-verifier = { version = "1.0.0", default-features = false
pallet-object-store = { version = "0.1.0", default-features = false, path = "../pallet-object-store" }
pallet-offences-subspace = { version = "0.1.0", default-features = false, path = "../pallet-offences-subspace" }
pallet-rewards = { version = "0.1.0", default-features = false, path = "../pallet-rewards" }
pallet-subspace = { version = "0.1.0", default-features = false, features = ["no-early-solution-range-updates"], path = "../pallet-subspace" }
pallet-subspace = { version = "0.1.0", default-features = false, path = "../pallet-subspace" }
pallet-sudo = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "c4f3d028621edb293d2c423516221aa396f76a2d" }
pallet-timestamp = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "c4f3d028621edb293d2c423516221aa396f76a2d" }
pallet-transaction-fees = { version = "0.1.0", default-features = false, path = "../pallet-transaction-fees" }
Expand Down
4 changes: 4 additions & 0 deletions crates/subspace-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,9 @@ impl frame_system::Config for Runtime {
parameter_types! {
pub const SlotProbability: (u64, u64) = SLOT_PROBABILITY;
pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK;
// Disable solution range adjustment at the start of chain.
// Root origin must enable later
pub const ShouldAdjustSolutionRange: bool = false;
}

impl pallet_subspace::Config for Runtime {
Expand All @@ -270,6 +273,7 @@ impl pallet_subspace::Config for Runtime {
type RecordSize = ConstU32<RECORD_SIZE>;
type MaxPlotSize = ConstU64<MAX_PLOT_SIZE>;
type RecordedHistorySegmentSize = ConstU32<RECORDED_HISTORY_SEGMENT_SIZE>;
type ShouldAdjustSolutionRange = ShouldAdjustSolutionRange;
type GlobalRandomnessIntervalTrigger = pallet_subspace::NormalGlobalRandomnessInterval;
type EraChangeTrigger = pallet_subspace::NormalEraChange;
type EonChangeTrigger = pallet_subspace::NormalEonChange;
Expand Down
2 changes: 2 additions & 0 deletions substrate/substrate-test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ impl pallet_babe::Config for Runtime {
parameter_types! {
pub const SlotProbability: (u64, u64) = (3, 10);
pub const ExpectedBlockTime: u64 = 10_000;
pub const ShouldAdjustSolutionRange: bool = false;
}

impl pallet_subspace::Config for Runtime {
Expand All @@ -666,6 +667,7 @@ impl pallet_subspace::Config for Runtime {
type RecordSize = ConstU32<3840>;
type MaxPlotSize = ConstU64<{ 10 * 1024 * 1024 * 1024 / PIECE_SIZE as u64 }>;
type RecordedHistorySegmentSize = ConstU32<{ 3840 * 256 / 2 }>;
type ShouldAdjustSolutionRange = ShouldAdjustSolutionRange;
type GlobalRandomnessIntervalTrigger = pallet_subspace::NormalGlobalRandomnessInterval;
type EraChangeTrigger = pallet_subspace::NormalEraChange;
type EonChangeTrigger = pallet_subspace::NormalEonChange;
Expand Down
2 changes: 1 addition & 1 deletion test/subspace-test-runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pallet-grandpa-finality-verifier = { version = "1.0.0", default-features = false
pallet-object-store = { version = "0.1.0", default-features = false, path = "../../crates/pallet-object-store" }
pallet-offences-subspace = { version = "0.1.0", default-features = false, path = "../../crates/pallet-offences-subspace" }
pallet-rewards = { version = "0.1.0", default-features = false, path = "../../crates/pallet-rewards" }
pallet-subspace = { version = "0.1.0", default-features = false, features = ["no-early-solution-range-updates"], path = "../../crates/pallet-subspace" }
pallet-subspace = { version = "0.1.0", default-features = false, path = "../../crates/pallet-subspace" }
pallet-sudo = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "c4f3d028621edb293d2c423516221aa396f76a2d" }
pallet-timestamp = { version = "4.0.0-dev", default-features = false, git = "https://github.com/paritytech/substrate", rev = "c4f3d028621edb293d2c423516221aa396f76a2d" }
pallet-transaction-fees = { version = "0.1.0", default-features = false, path = "../../crates/pallet-transaction-fees" }
Expand Down
2 changes: 2 additions & 0 deletions test/subspace-test-runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ impl frame_system::Config for Runtime {
parameter_types! {
pub const SlotProbability: (u64, u64) = SLOT_PROBABILITY;
pub const ExpectedBlockTime: Moment = MILLISECS_PER_BLOCK;
pub const ShouldAdjustSolutionRange: bool = false;
}

impl pallet_subspace::Config for Runtime {
Expand All @@ -245,6 +246,7 @@ impl pallet_subspace::Config for Runtime {
type RecordSize = ConstU32<RECORD_SIZE>;
type MaxPlotSize = ConstU64<MAX_PLOT_SIZE>;
type RecordedHistorySegmentSize = ConstU32<RECORDED_HISTORY_SEGMENT_SIZE>;
type ShouldAdjustSolutionRange = ShouldAdjustSolutionRange;
type GlobalRandomnessIntervalTrigger = pallet_subspace::NormalGlobalRandomnessInterval;
type EraChangeTrigger = pallet_subspace::NormalEraChange;
type EonChangeTrigger = pallet_subspace::NormalEonChange;
Expand Down

0 comments on commit e7a8a33

Please sign in to comment.