Set uncles inherent (#3317)

* Include uncles

* Filter missing uncles

* Moved inherent registration to a new crate

* Ignore invalid inherent encoding
This commit is contained in:
Arkadiy Paronyan
2019-08-08 00:56:29 +02:00
committed by GitHub
parent ea58b7c92a
commit 58bd0d4c05
12 changed files with 319 additions and 82 deletions
+175 -66
View File
@@ -20,7 +20,7 @@
#![cfg_attr(not(feature = "std"), no_std)]
use rstd::prelude::*;
use rstd::{result, prelude::*};
use rstd::collections::btree_set::BTreeSet;
use srml_support::{decl_module, decl_storage, for_each_tuple, StorageValue};
use srml_support::traits::{FindAuthor, VerifySeal, Get};
@@ -29,6 +29,61 @@ use codec::{Encode, Decode};
use system::ensure_none;
use sr_primitives::traits::{SimpleArithmetic, Header as HeaderT, One, Zero};
use sr_primitives::weights::SimpleDispatchInfo;
use inherents::{
RuntimeString, InherentIdentifier, ProvideInherent,
InherentData, MakeFatalError,
};
/// The identifier for the `uncles` inherent.
pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"uncles00";
/// Auxiliary trait to extract uncles inherent data.
pub trait UnclesInherentData<H: Decode> {
/// Get uncles.
fn uncles(&self) -> Result<Vec<H>, RuntimeString>;
}
impl<H: Decode> UnclesInherentData<H> for InherentData {
fn uncles(&self) -> Result<Vec<H>, RuntimeString> {
Ok(self.get_data(&INHERENT_IDENTIFIER)?.unwrap_or_default())
}
}
/// Provider for inherent data.
#[cfg(feature = "std")]
pub struct InherentDataProvider<F, H> {
inner: F,
_marker: std::marker::PhantomData<H>,
}
#[cfg(feature = "std")]
impl<F, H> InherentDataProvider<F, H> {
pub fn new(uncles_oracle: F) -> Self {
InherentDataProvider { inner: uncles_oracle, _marker: Default::default() }
}
}
#[cfg(feature = "std")]
impl<F, H: Encode + std::fmt::Debug> inherents::ProvideInherentData for InherentDataProvider<F, H>
where F: Fn() -> Vec<H>
{
fn inherent_identifier(&self) -> &'static InherentIdentifier {
&INHERENT_IDENTIFIER
}
fn provide_inherent_data(&self, inherent_data: &mut InherentData) -> Result<(), RuntimeString> {
let uncles = (self.inner)();
if !uncles.is_empty() {
inherent_data.put_data(INHERENT_IDENTIFIER, &uncles)
} else {
Ok(())
}
}
fn error_to_string(&self, _error: &[u8]) -> Option<String> {
Some(format!("no further information"))
}
}
pub trait Trait: system::Trait {
/// Find the author of a block.
@@ -101,16 +156,16 @@ pub trait FilterUncle<Header, Author> {
/// Do additional filtering on a seal-checked uncle block, with the accumulated
/// filter.
fn filter_uncle(header: &Header, acc: Self::Accumulator)
-> Result<(Option<Author>, Self::Accumulator), &'static str>;
fn filter_uncle(header: &Header, acc: &mut Self::Accumulator)
-> Result<Option<Author>, &'static str>;
}
impl<H, A> FilterUncle<H, A> for () {
type Accumulator = ();
fn filter_uncle(_: &H, acc: Self::Accumulator)
-> Result<(Option<A>, Self::Accumulator), &'static str>
fn filter_uncle(_: &H, _acc: &mut Self::Accumulator)
-> Result<Option<A>, &'static str>
{
Ok((None, acc))
Ok(None)
}
}
@@ -124,10 +179,10 @@ impl<Header, Author, T: VerifySeal<Header, Author>> FilterUncle<Header, Author>
{
type Accumulator = ();
fn filter_uncle(header: &Header, _acc: ())
-> Result<(Option<Author>, ()), &'static str>
fn filter_uncle(header: &Header, _acc: &mut ())
-> Result<Option<Author>, &'static str>
{
T::verify_seal(header).map(|author| (author, ()))
T::verify_seal(header)
}
}
@@ -147,8 +202,8 @@ where
{
type Accumulator = BTreeSet<(Header::Number, Author)>;
fn filter_uncle(header: &Header, mut acc: Self::Accumulator)
-> Result<(Option<Author>, Self::Accumulator), &'static str>
fn filter_uncle(header: &Header, acc: &mut Self::Accumulator)
-> Result<Option<Author>, &'static str>
{
let author = T::verify_seal(header)?;
let number = header.number();
@@ -159,7 +214,7 @@ where
}
}
Ok((author, acc))
Ok(author)
}
}
@@ -256,6 +311,39 @@ impl<T: Trait> Module<T> {
fn verify_and_import_uncles(new_uncles: Vec<T::Header>) -> DispatchResult {
let now = <system::Module<T>>::block_number();
let mut uncles = <Self as Store>::Uncles::get();
uncles.push(UncleEntryItem::InclusionHeight(now));
let mut acc: <T::FilterUncle as FilterUncle<_, _>>::Accumulator = Default::default();
for uncle in new_uncles {
let prev_uncles = uncles.iter().filter_map(|entry|
match entry {
UncleEntryItem::InclusionHeight(_) => None,
UncleEntryItem::Uncle(h, _) => Some(h),
});
let author = Self::verify_uncle(&uncle, prev_uncles, &mut acc)?;
let hash = uncle.hash();
T::EventHandler::note_uncle(
author.clone().unwrap_or_default(),
now - uncle.number().clone(),
);
uncles.push(UncleEntryItem::Uncle(hash, author));
}
<Self as Store>::Uncles::put(&uncles);
Ok(())
}
fn verify_uncle<'a, I: IntoIterator<Item=&'a T::Hash>>(
uncle: &T::Header,
existing_uncles: I,
accumulator: &mut <T::FilterUncle as FilterUncle<T::Header, T::AccountId>>::Accumulator,
) -> Result<Option<T::AccountId>, &'static str>
{
let now = <system::Module<T>>::block_number();
let (minimum_height, maximum_height) = {
let uncle_generations = T::UncleGenerations::get();
let min = if now >= uncle_generations {
@@ -267,55 +355,82 @@ impl<T: Trait> Module<T> {
(min, now)
};
let mut uncles = <Self as Store>::Uncles::get();
uncles.push(UncleEntryItem::InclusionHeight(now));
let hash = uncle.hash();
let mut acc: <T::FilterUncle as FilterUncle<_, _>>::Accumulator = Default::default();
for uncle in new_uncles {
let hash = uncle.hash();
if uncle.number() < &One::one() {
return Err("uncle is genesis");
}
if uncle.number() > &maximum_height {
return Err("uncles too high in chain");
}
{
let parent_number = uncle.number().clone() - One::one();
let parent_hash = <system::Module<T>>::block_hash(&parent_number);
if &parent_hash != uncle.parent_hash() {
return Err("uncle parent not in chain");
}
}
if uncle.number() < &minimum_height {
return Err("uncle not recent enough to be included");
}
let duplicate = uncles.iter().find(|entry| match entry {
UncleEntryItem::InclusionHeight(_) => false,
UncleEntryItem::Uncle(h, _) => h == &hash,
}).is_some();
let in_chain = <system::Module<T>>::block_hash(uncle.number()) == hash;
if duplicate || in_chain { return Err("uncle already included") }
// check uncle validity.
let (author, temp_acc) = T::FilterUncle::filter_uncle(&uncle, acc)?;
acc = temp_acc;
T::EventHandler::note_uncle(
author.clone().unwrap_or_default(),
now - uncle.number().clone(),
);
uncles.push(UncleEntryItem::Uncle(hash, author));
if uncle.number() < &One::one() {
return Err("uncle is genesis");
}
<Self as Store>::Uncles::put(&uncles);
if uncle.number() > &maximum_height {
return Err("uncle is too high in chain");
}
{
let parent_number = uncle.number().clone() - One::one();
let parent_hash = <system::Module<T>>::block_hash(&parent_number);
if &parent_hash != uncle.parent_hash() {
return Err("uncle parent not in chain");
}
}
if uncle.number() < &minimum_height {
return Err("uncle not recent enough to be included");
}
let duplicate = existing_uncles.into_iter().find(|h| **h == hash).is_some();
let in_chain = <system::Module<T>>::block_hash(uncle.number()) == hash;
if duplicate || in_chain {
return Err("uncle already included")
}
// check uncle validity.
T::FilterUncle::filter_uncle(&uncle, accumulator)
}
}
impl<T: Trait> ProvideInherent for Module<T> {
type Call = Call<T>;
type Error = MakeFatalError<()>;
const INHERENT_IDENTIFIER: InherentIdentifier = INHERENT_IDENTIFIER;
fn create_inherent(data: &InherentData) -> Option<Self::Call> {
let uncles = data.uncles().unwrap_or_default();
let mut set_uncles = Vec::new();
if !uncles.is_empty() {
let prev_uncles = <Self as Store>::Uncles::get();
let mut existing_hashes: Vec<_> = prev_uncles.into_iter().filter_map(|entry|
match entry {
UncleEntryItem::InclusionHeight(_) => None,
UncleEntryItem::Uncle(h, _) => Some(h),
}
).collect();
let mut acc: <T::FilterUncle as FilterUncle<_, _>>::Accumulator = Default::default();
for uncle in uncles {
match Self::verify_uncle(&uncle, &existing_hashes, &mut acc) {
Ok(_) => {
let hash = uncle.hash();
set_uncles.push(uncle);
existing_hashes.push(hash);
}
Err(_) => {
// skip this uncle
}
}
}
}
if set_uncles.is_empty() {
None
} else {
Some(Call::set_uncles(set_uncles))
}
}
fn check_inherent(_call: &Self::Call, _data: &InherentData) -> result::Result<(), Self::Error> {
Ok(())
}
}
@@ -601,7 +716,7 @@ mod tests {
let author_a = 42;
let author_b = 43;
let mut acc: Option<<Filter as FilterUncle<Header, u64>>::Accumulator> = Some(Default::default());
let mut acc: <Filter as FilterUncle<Header, u64>>::Accumulator = Default::default();
let header_a1 = seal_header(
create_header(1, Default::default(), [1; 32].into()),
author_a,
@@ -621,13 +736,7 @@ mod tests {
);
let mut check_filter = move |uncle| {
match Filter::filter_uncle(uncle, acc.take().unwrap()) {
Ok((author, a)) => {
acc = Some(a);
Ok(author)
}
Err(e) => Err(e),
}
Filter::filter_uncle(uncle, &mut acc)
};
// same height, different author is OK.