Skip to content

Commit 34a3e95

Browse files
authored
Merge pull request #1939 from subspace/pot-entropy-injection
Implement PoT entropy injection
2 parents 19dc65f + b000948 commit 34a3e95

9 files changed

Lines changed: 236 additions & 25 deletions

File tree

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/pallet-subspace/src/lib.rs

Lines changed: 163 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![feature(assert_matches, const_option)]
21
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
32
// Copyright (C) 2021 Subspace Labs, Inc.
43
// SPDX-License-Identifier: Apache-2.0
@@ -16,6 +15,7 @@
1615
// limitations under the License.
1716
#![doc = include_str!("../README.md")]
1817
#![cfg_attr(not(feature = "std"), no_std)]
18+
#![feature(assert_matches, const_option, let_chains)]
1919
#![warn(unused_must_use, unsafe_code, unused_variables, unused_must_use)]
2020

2121
extern crate alloc;
@@ -50,12 +50,14 @@ use sp_consensus_slots::Slot;
5050
use sp_consensus_subspace::consensus::verify_solution;
5151
use sp_consensus_subspace::digests::CompatibleDigestItem;
5252
use sp_consensus_subspace::offence::{OffenceDetails, OffenceError, OnOffenceHandler};
53-
#[cfg(feature = "pot")]
54-
use sp_consensus_subspace::PotParameters;
5553
use sp_consensus_subspace::{
5654
ChainConstants, EquivocationProof, FarmerPublicKey, FarmerSignature, SignedVote, Vote,
5755
};
56+
#[cfg(feature = "pot")]
57+
use sp_consensus_subspace::{PotParameters, PotParametersChange};
5858
use sp_runtime::generic::DigestItem;
59+
#[cfg(feature = "pot")]
60+
use sp_runtime::traits::CheckedSub;
5961
use sp_runtime::traits::{BlockNumberProvider, Hash, One, Zero};
6062
#[cfg(not(feature = "pot"))]
6163
use sp_runtime::traits::{SaturatedConversion, Saturating};
@@ -69,11 +71,15 @@ use sp_std::prelude::*;
6971
use subspace_core_primitives::crypto::Scalar;
7072
#[cfg(feature = "pot")]
7173
use subspace_core_primitives::PotProof;
74+
#[cfg(not(feature = "pot"))]
75+
use subspace_core_primitives::SlotNumber;
7276
use subspace_core_primitives::{
7377
ArchivedHistorySegment, HistorySize, PublicKey, Randomness, RewardSignature, SectorId,
7478
SectorIndex, SegmentHeader, SegmentIndex, SolutionRange,
7579
};
7680
use subspace_solving::REWARD_SIGNING_CONTEXT;
81+
#[cfg(feature = "pot")]
82+
use subspace_verification::derive_pot_entropy;
7783
#[cfg(not(feature = "pot"))]
7884
use subspace_verification::derive_randomness;
7985
use subspace_verification::{
@@ -152,7 +158,8 @@ mod pallet {
152158
use sp_std::prelude::*;
153159
use subspace_core_primitives::crypto::Scalar;
154160
use subspace_core_primitives::{
155-
HistorySize, Randomness, SectorIndex, SegmentHeader, SegmentIndex, SolutionRange,
161+
Blake3Hash, HistorySize, Randomness, SectorIndex, SegmentHeader, SegmentIndex,
162+
SolutionRange,
156163
};
157164

158165
pub(super) struct InitialSolutionRanges<T: Config> {
@@ -200,10 +207,25 @@ mod pallet {
200207
// TODO: Remove when switching to PoT by default
201208
type GlobalRandomnessUpdateInterval: Get<Self::BlockNumber>;
202209

203-
/// The amount of time, in blocks, between updates of global randomness.
210+
/// Number of slots between slot arrival and when corresponding block can be produced.
211+
///
212+
/// Practically this means future proof of time proof needs to be revealed this many slots
213+
/// ahead before block can be authored even though solution is available before that.
204214
#[pallet::constant]
205215
type BlockAuthoringDelay: Get<Slot>;
206216

217+
/// Interval, in blocks, between blockchain entropy injection into proof of time chain.
218+
#[pallet::constant]
219+
type PotEntropyInjectionInterval: Get<Self::BlockNumber>;
220+
221+
/// Interval, in entropy injection intervals, where to take entropy for injection from.
222+
#[pallet::constant]
223+
type PotEntropyInjectionLookbackDepth: Get<u8>;
224+
225+
/// Delay after block, in slots, when entropy injection takes effect.
226+
#[pallet::constant]
227+
type PotEntropyInjectionDelay: Get<Slot>;
228+
207229
/// The amount of time, in blocks, that each era should last.
208230
/// NOTE: Currently it is not possible to change the era duration after
209231
/// the chain has started. Attempting to do so will brick block production.
@@ -292,6 +314,13 @@ mod pallet {
292314
RootFarmer(FarmerPublicKey),
293315
}
294316

317+
#[derive(Debug, Copy, Clone, Encode, Decode, TypeInfo)]
318+
pub(super) struct PotEntropyValue {
319+
/// Target slot at which entropy should be injected (when known)
320+
pub(super) target_slot: Option<Slot>,
321+
pub(super) entropy: Blake3Hash,
322+
}
323+
295324
#[pallet::genesis_config]
296325
pub struct GenesisConfig {
297326
/// Whether rewards should be enabled.
@@ -469,6 +498,12 @@ mod pallet {
469498
>,
470499
>;
471500

501+
/// Entropy that needs to be injected into proof of time chain at specific slot associated with
502+
/// block number it came from.
503+
#[pallet::storage]
504+
pub(super) type PotEntropy<T: Config> =
505+
StorageValue<_, BTreeMap<T::BlockNumber, PotEntropyValue>, ValueQuery>;
506+
472507
/// The current block randomness, updated at block initialization. When the proof of time feature
473508
/// is enabled it derived from PoT otherwise PoR.
474509
#[pallet::storage]
@@ -784,7 +819,7 @@ impl<T: Config> Pallet<T> {
784819
}
785820

786821
fn do_initialize(block_number: T::BlockNumber) {
787-
let pre_digest = <frame_system::Pallet<T>>::digest()
822+
let pre_digest = frame_system::Pallet::<T>::digest()
788823
.logs
789824
.iter()
790825
.find_map(|s| s.as_subspace_pre_digest::<T::AccountId>())
@@ -891,7 +926,8 @@ impl<T: Config> Pallet<T> {
891926

892927
// Extract PoR randomness from pre-digest.
893928
#[cfg(not(feature = "pot"))]
894-
let block_randomness = derive_randomness(pre_digest.solution(), pre_digest.slot().into());
929+
let block_randomness =
930+
derive_randomness(pre_digest.solution(), SlotNumber::from(pre_digest.slot()));
895931

896932
#[cfg(feature = "pot")]
897933
let block_randomness = pre_digest
@@ -926,11 +962,90 @@ impl<T: Config> Pallet<T> {
926962
));
927963
}
928964

929-
// TODO: Take adjustment of iterations into account once we have it
930965
#[cfg(feature = "pot")]
931-
frame_system::Pallet::<T>::deposit_log(DigestItem::pot_slot_iterations(
932-
PotSlotIterations::<T>::get().expect("Always instantiated during genesis; qed"),
933-
));
966+
{
967+
let pot_slot_iterations =
968+
PotSlotIterations::<T>::get().expect("Always initialized during genesis; qed");
969+
let pot_entropy_injection_interval = T::PotEntropyInjectionInterval::get();
970+
let pot_entropy_injection_delay = T::PotEntropyInjectionDelay::get();
971+
972+
// TODO: Take adjustment of iterations into account once we have it
973+
frame_system::Pallet::<T>::deposit_log(DigestItem::pot_slot_iterations(
974+
pot_slot_iterations,
975+
));
976+
977+
let mut entropy = PotEntropy::<T>::get();
978+
let lookback_in_blocks = pot_entropy_injection_interval
979+
* T::BlockNumber::from(T::PotEntropyInjectionLookbackDepth::get());
980+
let last_entropy_injection_block =
981+
block_number / pot_entropy_injection_interval * pot_entropy_injection_interval;
982+
let maybe_entropy_source_block_number =
983+
last_entropy_injection_block.checked_sub(&lookback_in_blocks);
984+
985+
if (block_number % pot_entropy_injection_interval).is_zero() {
986+
let current_block_entropy = derive_pot_entropy(
987+
pre_digest.solution().chunk,
988+
pre_digest.pot_info().proof_of_time(),
989+
);
990+
// Collect entropy every `T::PotEntropyInjectionInterval` blocks
991+
entropy.insert(
992+
block_number,
993+
PotEntropyValue {
994+
target_slot: None,
995+
entropy: current_block_entropy,
996+
},
997+
);
998+
999+
// Update target slot for entropy injection once we know it
1000+
if let Some(entropy_source_block_number) = maybe_entropy_source_block_number {
1001+
if let Some(entropy_value) = entropy.get_mut(&entropy_source_block_number) {
1002+
let target_slot = pre_digest
1003+
.slot()
1004+
.saturating_add(pot_entropy_injection_delay);
1005+
debug!(
1006+
target: "runtime::subspace",
1007+
"Pot entropy injection will happen at slot {target_slot:?}",
1008+
);
1009+
entropy_value.target_slot.replace(target_slot);
1010+
}
1011+
}
1012+
1013+
PotEntropy::<T>::put(entropy.clone());
1014+
}
1015+
1016+
// Deposit consensus log item with parameters change in case corresponding entropy is
1017+
// available
1018+
if let Some(entropy_source_block_number) = maybe_entropy_source_block_number {
1019+
let maybe_entropy_value = entropy.get(&entropy_source_block_number).copied();
1020+
if let Some(PotEntropyValue {
1021+
target_slot,
1022+
entropy,
1023+
}) = maybe_entropy_value
1024+
{
1025+
let target_slot = target_slot
1026+
.expect("Target slot is guaranteed to be present due to logic above; qed");
1027+
1028+
frame_system::Pallet::<T>::deposit_log(DigestItem::pot_parameters_change(
1029+
PotParametersChange {
1030+
slot: target_slot,
1031+
// TODO: Take adjustment of iterations into account once we have it
1032+
slot_iterations: pot_slot_iterations,
1033+
entropy,
1034+
},
1035+
));
1036+
}
1037+
}
1038+
1039+
// Clean up old values we'll no longer need
1040+
if let Some(entry) = entropy.first_entry() {
1041+
if let Some(target_slot) = entry.get().target_slot
1042+
&& target_slot < pre_digest.slot()
1043+
{
1044+
entry.remove();
1045+
PotEntropy::<T>::put(entropy);
1046+
}
1047+
}
1048+
}
9341049
}
9351050

9361051
fn do_finalize(_block_number: T::BlockNumber) {
@@ -1084,12 +1199,44 @@ impl<T: Config> Pallet<T> {
10841199
/// Proof of time parameters
10851200
#[cfg(feature = "pot")]
10861201
pub fn pot_parameters() -> PotParameters {
1202+
let block_number = frame_system::Pallet::<T>::block_number();
1203+
let pot_slot_iterations =
1204+
PotSlotIterations::<T>::get().expect("Always initialized during genesis; qed");
1205+
let pot_entropy_injection_interval = T::PotEntropyInjectionInterval::get();
1206+
1207+
let entropy = PotEntropy::<T>::get();
1208+
let lookback_in_blocks = pot_entropy_injection_interval
1209+
* T::BlockNumber::from(T::PotEntropyInjectionLookbackDepth::get());
1210+
let last_entropy_injection_block =
1211+
block_number / pot_entropy_injection_interval * pot_entropy_injection_interval;
1212+
let maybe_entropy_source_block_number =
1213+
last_entropy_injection_block.checked_sub(&lookback_in_blocks);
1214+
1215+
let mut next_change = None;
1216+
1217+
if let Some(entropy_source_block_number) = maybe_entropy_source_block_number {
1218+
let maybe_entropy_value = entropy.get(&entropy_source_block_number).copied();
1219+
if let Some(PotEntropyValue {
1220+
target_slot,
1221+
entropy,
1222+
}) = maybe_entropy_value
1223+
{
1224+
let target_slot = target_slot.expect(
1225+
"Always present due to identical check present in block initialization; qed",
1226+
);
1227+
1228+
next_change.replace(PotParametersChange {
1229+
slot: target_slot,
1230+
// TODO: Take adjustment of iterations into account once we have it
1231+
slot_iterations: pot_slot_iterations,
1232+
entropy,
1233+
});
1234+
}
1235+
}
1236+
10871237
PotParameters::V0 {
1088-
slot_iterations: PotSlotIterations::<T>::get()
1089-
.expect("Always instantiated during genesis; qed"),
1090-
// TODO: This is where adjustment for number of iterations and entropy injection will
1091-
// happen for runtime API calls
1092-
next_change: None,
1238+
slot_iterations: pot_slot_iterations,
1239+
next_change,
10931240
}
10941241
}
10951242

crates/pallet-subspace/src/mock.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ use subspace_archiving::archiver::{Archiver, NewArchivedSegment};
4545
use subspace_core_primitives::crypto::kzg::{embedded_kzg_settings, Kzg};
4646
use subspace_core_primitives::crypto::Scalar;
4747
use subspace_core_primitives::{
48-
ArchivedBlockProgress, ArchivedHistorySegment, Blake2b256Hash, HistorySize, LastArchivedBlock,
49-
Piece, PieceOffset, PublicKey, Randomness, RecordedHistorySegment, SegmentCommitment,
50-
SegmentHeader, SegmentIndex, SlotNumber, Solution, SolutionRange,
48+
ArchivedBlockProgress, ArchivedHistorySegment, Blake2b256Hash, BlockNumber, HistorySize,
49+
LastArchivedBlock, Piece, PieceOffset, PublicKey, Randomness, RecordedHistorySegment,
50+
SegmentCommitment, SegmentHeader, SegmentIndex, SlotNumber, Solution, SolutionRange,
5151
};
5252
use subspace_erasure_coding::ErasureCoding;
5353
use subspace_farmer_components::auditing::audit_sector;
@@ -162,6 +162,9 @@ parameter_types! {
162162
#[cfg(not(feature = "pot"))]
163163
pub const GlobalRandomnessUpdateInterval: u64 = 10;
164164
pub const BlockAuthoringDelay: SlotNumber = 2;
165+
pub const PotEntropyInjectionInterval: BlockNumber = 5;
166+
pub const PotEntropyInjectionLookbackDepth: u8 = 2;
167+
pub const PotEntropyInjectionDelay: SlotNumber = 4;
165168
pub const EraDuration: u32 = 4;
166169
// 1GB
167170
pub const InitialSolutionRange: SolutionRange = INITIAL_SOLUTION_RANGE;
@@ -184,6 +187,9 @@ impl Config for Test {
184187
type RuntimeEvent = RuntimeEvent;
185188
type GlobalRandomnessUpdateInterval = GlobalRandomnessUpdateInterval;
186189
type BlockAuthoringDelay = BlockAuthoringDelay;
190+
type PotEntropyInjectionInterval = PotEntropyInjectionInterval;
191+
type PotEntropyInjectionLookbackDepth = PotEntropyInjectionLookbackDepth;
192+
type PotEntropyInjectionDelay = PotEntropyInjectionDelay;
187193
type EraDuration = EraDuration;
188194
type InitialSolutionRange = InitialSolutionRange;
189195
type SlotProbability = SlotProbability;

crates/sp-consensus-subspace/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ use sp_runtime_interface::{pass_by, runtime_interface};
4343
#[cfg(feature = "pot")]
4444
use sp_std::num::NonZeroU32;
4545
use sp_std::vec::Vec;
46+
#[cfg(feature = "std")]
4647
use subspace_core_primitives::crypto::kzg::Kzg;
4748
#[cfg(feature = "pot")]
4849
use subspace_core_primitives::Blake3Hash;
@@ -59,6 +60,7 @@ use subspace_proof_of_space::chia::ChiaTable;
5960
use subspace_proof_of_space::shim::ShimTable;
6061
#[cfg(feature = "std")]
6162
use subspace_proof_of_space::PosTableType;
63+
#[cfg(feature = "std")]
6264
use subspace_proof_of_space::Table;
6365
use subspace_solving::REWARD_SIGNING_CONTEXT;
6466
use subspace_verification::{check_reward_signature, VerifySolutionParams};

crates/subspace-runtime/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ sp-session = { version = "4.0.0-dev", default-features = false, git = "https://g
5858
sp-std = { version = "8.0.0", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" }
5959
sp-transaction-pool = { version = "4.0.0-dev", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" }
6060
sp-version = { version = "22.0.0", default-features = false, git = "https://github.com/subspace/substrate", rev = "55c157cff49b638a59d81a9f971f0f9a66829c71" }
61+
static_assertions = "1.1.0"
6162
subspace-core-primitives = { version = "0.1.0", default-features = false, path = "../subspace-core-primitives" }
6263
subspace-runtime-primitives = { version = "0.1.0", default-features = false, path = "../subspace-runtime-primitives" }
6364
subspace-verification = { version = "0.1.0", default-features = false, path = "../subspace-verification" }

0 commit comments

Comments
 (0)