fix BABE randomness calculation (#3305)

This commit is contained in:
Robert Habermeier
2019-08-06 02:50:37 +02:00
committed by DemiMarie-parity
parent a7c6950f1b
commit d5726d8e8a
2 changed files with 48 additions and 12 deletions
+2 -2
View File
@@ -79,8 +79,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
// and set impl_version to equal spec_version. If only runtime
// implementation changes and behavior does not, then leave spec_version as
// is and increment impl_version.
spec_version: 127,
impl_version: 128,
spec_version: 129,
impl_version: 129,
apis: RUNTIME_API_VERSIONS,
};
+46 -10
View File
@@ -22,7 +22,7 @@
pub use timestamp;
use rstd::{result, prelude::*};
use srml_support::{decl_storage, decl_module, StorageValue, traits::FindAuthor, traits::Get};
use srml_support::{decl_storage, decl_module, StorageValue, StorageMap, traits::FindAuthor, traits::Get};
use timestamp::{OnTimestampSet};
use sr_primitives::{generic::DigestItem, ConsensusEngineId};
use sr_primitives::traits::{IsMember, SaturatedConversion, Saturating, RandomnessBeacon, Convert};
@@ -115,6 +115,8 @@ pub trait Trait: timestamp::Trait {
/// The length of the BABE randomness
pub const RANDOMNESS_LENGTH: usize = 32;
const UNDER_CONSTRUCTION_SEGMENT_LENGTH: usize = 256;
decl_storage! {
trait Store for Module<T: Trait> as Babe {
/// Current epoch index.
@@ -150,7 +152,16 @@ decl_storage! {
NextRandomness: [u8; 32 /* RANDOMNESS_LENGTH */];
/// Randomness under construction.
UnderConstruction: [u8; 32 /* VRF_OUTPUT_LENGTH */];
///
/// We make a tradeoff between storage accesses and list length.
/// We store the under-construction randomness in segments of up to
/// `UNDER_CONSTRUCTION_SEGMENT_LENGTH`.
///
/// Once a segment reaches this length, we begin the next one.
/// We reset all segments and return to `0` at the beginning of every
/// epoch.
SegmentIndex build(|_| 0): u32;
UnderConstruction: map u32 => Vec<[u8; 32 /* VRF_OUTPUT_LENGTH */]>;
}
}
@@ -248,19 +259,35 @@ impl<T: Trait> Module<T> {
}
fn deposit_vrf_output(vrf_output: &[u8; VRF_OUTPUT_LENGTH]) {
UnderConstruction::mutate(|z| z.iter_mut().zip(vrf_output).for_each(|(x, y)| *x^=y))
let segment_idx = <SegmentIndex>::get();
let mut segment = <UnderConstruction>::get(&segment_idx);
if segment.len() < UNDER_CONSTRUCTION_SEGMENT_LENGTH {
// push onto current segment: not full.
segment.push(*vrf_output);
<UnderConstruction>::insert(&segment_idx, &segment);
} else {
// move onto the next segment and update the index.
let segment_idx = segment_idx + 1;
<UnderConstruction>::insert(&segment_idx, vec![*vrf_output].as_ref());
<SegmentIndex>::put(&segment_idx);
}
}
/// Call this function exactly once when an epoch changes, to update the
/// randomness. Returns the new randomness.
fn randomness_change_epoch(next_epoch_index: u64) -> [u8; RANDOMNESS_LENGTH] {
let this_randomness = NextRandomness::get();
let segment_idx: u32 = <SegmentIndex>::mutate(|s| rstd::mem::replace(s, 0));
// overestimate to the segment being full.
let rho_size = segment_idx.saturating_add(1) as usize * UNDER_CONSTRUCTION_SEGMENT_LENGTH;
let next_randomness = compute_randomness(
this_randomness,
next_epoch_index,
UnderConstruction::get(),
(0..segment_idx).flat_map(|i| <UnderConstruction>::take(&i)),
Some(rho_size),
);
UnderConstruction::put(&[0; RANDOMNESS_LENGTH]);
NextRandomness::put(&next_randomness);
this_randomness
}
@@ -345,15 +372,24 @@ impl<T: Trait + staking::Trait> session::OneSessionHandler<T::AccountId> for Mod
}
}
// compute randomness for a new epoch. rho is the concatenation of all
// VRF outputs in the prior epoch.
//
// an optional size hint as to how many VRF outputs there were may be provided.
fn compute_randomness(
last_epoch_randomness: [u8; RANDOMNESS_LENGTH],
epoch_index: u64,
rho: [u8; VRF_OUTPUT_LENGTH],
rho: impl Iterator<Item=[u8; VRF_OUTPUT_LENGTH]>,
rho_size_hint: Option<usize>,
) -> [u8; RANDOMNESS_LENGTH] {
let mut s = [0; 40 + VRF_OUTPUT_LENGTH];
s[..32].copy_from_slice(&last_epoch_randomness);
s[32..40].copy_from_slice(&epoch_index.to_le_bytes());
s[40..].copy_from_slice(&rho);
let mut s = Vec::with_capacity(40 + rho_size_hint.unwrap_or(0) * VRF_OUTPUT_LENGTH);
s.extend_from_slice(&last_epoch_randomness);
s.extend_from_slice(&epoch_index.to_le_bytes());
for vrf_output in rho {
s.extend_from_slice(&vrf_output[..]);
}
runtime_io::blake2_256(&s)
}