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