// Copyright 2019 Parity Technologies (UK) Ltd.
// This file is part of Polkadot.
// Polkadot is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Polkadot is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Polkadot. If not, see .
//! A module for tracking all attestations that fell on a given candidate receipt.
//!
//! In the future, it is planned that this module will handle dispute resolution
//! as well.
use rstd::prelude::*;
use codec::{Encode, Decode};
use srml_support::{decl_storage, decl_module, ensure};
use primitives::{Hash, parachain::{AttestedCandidate, CandidateReceipt, Id as ParaId}};
use {system, session::{self, SessionIndex}};
use srml_support::{
StorageValue, StorageMap, StorageDoubleMap, dispatch::Result, traits::Get,
};
use inherents::{ProvideInherent, InherentData, RuntimeString, MakeFatalError, InherentIdentifier};
use system::ensure_none;
/// Parachain blocks included in a recent relay-chain block.
#[derive(Encode, Decode)]
pub struct IncludedBlocks {
/// The actual relay chain block number where blocks were included.
pub actual_number: T::BlockNumber,
/// The session index at this block.
pub session: SessionIndex,
/// The randomness seed at this block.
pub random_seed: [u8; 32],
/// All parachain IDs active at this block.
pub active_parachains: Vec,
/// Hashes of the parachain candidates included at this block.
pub para_blocks: Vec,
}
/// Attestations kept over time on a parachain block.
#[derive(Encode, Decode)]
pub struct BlockAttestations {
receipt: CandidateReceipt,
valid: Vec, // stash account ID of voter.
invalid: Vec, // stash account ID of voter.
}
/// Additional attestations on a parachain block, after it was included.
#[derive(Encode, Decode, Clone, PartialEq)]
#[cfg_attr(feature = "std", derive(Debug))]
pub struct MoreAttestations;
pub trait Trait: session::Trait {
/// How many blocks ago we're willing to accept attestations for.
type AttestationPeriod: Get;
/// Get a list of the validators' underlying identities.
type ValidatorIdentities: Get>;
}
decl_storage! {
trait Store for Module as Attestations {
/// A mapping from modular block number (n % AttestationPeriod)
/// to session index and the list of candidate hashes.
pub RecentParaBlocks: map T::BlockNumber => Option>;
/// Attestations on a recent parachain block.
pub ParaBlockAttestations: double_map T::BlockNumber, blake2_128(Hash) => Option>;
// Did we already have more attestations included in this block?
DidUpdate: bool;
}
}
decl_module! {
/// Parachain-attestations module.
pub struct Module for enum Call where origin: ::Origin {
/// Provide candidate receipts for parachains, in ascending order by id.
fn more_attestations(origin, _more: MoreAttestations) -> Result {
ensure_none(origin)?;
ensure!(!::exists(), "More attestations can be added only once in a block.");
::put(true);
Ok(())
}
fn on_finalize(_n: T::BlockNumber) {
::kill();
}
}
}
impl Module {
/// Update recent candidates to contain the already-checked parachain candidates.
pub(crate) fn note_included(heads: &[AttestedCandidate], para_blocks: IncludedBlocks) {
let attestation_period = T::AttestationPeriod::get();
let mod_num = para_blocks.actual_number % attestation_period;
// clear old entry that was in this place.
if let Some(old_entry) = >::take(&mod_num) {
>::remove_prefix(&old_entry.actual_number);
}
let validators = T::ValidatorIdentities::get();
// make new entry.
for (head, hash) in heads.iter().zip(¶_blocks.para_blocks) {
let mut valid = Vec::new();
let invalid = Vec::new();
for (auth_index, _) in head.validator_indices
.iter()
.enumerate()
.filter(|(_, bit)| *bit)
{
let stash_id = validators.get(auth_index)
.expect("auth_index checked to be within bounds in `check_candidates`; qed")
.clone();
valid.push(stash_id);
}
let summary = BlockAttestations {
receipt: head.candidate().clone(),
valid,
invalid,
};
>::insert(¶_blocks.actual_number, hash, &summary);
}
>::insert(&mod_num, ¶_blocks);
}
}
/// An identifier for inherent data that provides after-the-fact attestations
/// on already included parachain blocks.
pub const MORE_ATTESTATIONS_IDENTIFIER: InherentIdentifier = *b"par-atts";
pub type InherentType = MoreAttestations;
impl ProvideInherent for Module {
type Call = Call;
type Error = MakeFatalError;
const INHERENT_IDENTIFIER: InherentIdentifier = MORE_ATTESTATIONS_IDENTIFIER;
fn create_inherent(data: &InherentData) -> Option {
data.get_data::(&MORE_ATTESTATIONS_IDENTIFIER)
.ok()
.and_then(|x| x.map(Call::more_attestations))
}
}