attestations.rs 6.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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 <http://www.gnu.org/licenses/>.

//! 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::*;
23
use codec::{Encode, Decode};
24
use paint_support::{decl_storage, decl_module, ensure, dispatch::Result, traits::Get};
25
26

use primitives::{Hash, parachain::{AttestedCandidate, CandidateReceipt, Id as ParaId}};
27
use sr_primitives::RuntimeDebug;
28
use sr_staking_primitives::SessionIndex;
29

Gavin Wood's avatar
Gavin Wood committed
30
use inherents::{ProvideInherent, InherentData, MakeFatalError, InherentIdentifier};
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
use system::ensure_none;

/// Parachain blocks included in a recent relay-chain block.
#[derive(Encode, Decode)]
pub struct IncludedBlocks<T: Trait> {
	/// 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<ParaId>,
	/// Hashes of the parachain candidates included at this block.
	pub para_blocks: Vec<Hash>,
}

/// Attestations kept over time on a parachain block.
#[derive(Encode, Decode)]
pub struct BlockAttestations<T: Trait> {
	receipt: CandidateReceipt,
	valid: Vec<T::AccountId>, // stash account ID of voter.
	invalid: Vec<T::AccountId>, // stash account ID of voter.
}

/// Additional attestations on a parachain block, after it was included.
57
#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug)]
58
59
pub struct MoreAttestations;

60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
/// Something which processes rewards for received attestations.
pub trait RewardAttestation {
	/// Reward immediate attestations on parachain blocks. The argument is an iterable of
	/// validator indices of the attesting validators.
	fn reward_immediate(validator_indices: impl IntoIterator<Item=u32>);
}

impl RewardAttestation for () {
	fn reward_immediate(validator_indices: impl IntoIterator<Item=u32>) {
		// ensure side-effecting iterators do work.
		for _ in validator_indices {}
	}
}

impl<T: staking::Trait> RewardAttestation for staking::Module<T> {
	fn reward_immediate(validator_indices: impl IntoIterator<Item=u32>) {
		// The number of points to reward for a validity statement.
		// https://research.web3.foundation/en/latest/polkadot/Token%20Economics/#payment-details
		const STAKING_REWARD_POINTS: u32 = 20;

		Self::reward_by_indices(validator_indices.into_iter().map(|i| (i, STAKING_REWARD_POINTS)))
	}
}

84
85
86
87
88
89
pub trait Trait: session::Trait {
	/// How many blocks ago we're willing to accept attestations for.
	type AttestationPeriod: Get<Self::BlockNumber>;

	/// Get a list of the validators' underlying identities.
	type ValidatorIdentities: Get<Vec<Self::AccountId>>;
90
91
92

	/// Hook for rewarding validators upon attesting.
	type RewardAttestation: RewardAttestation;
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
}

decl_storage! {
	trait Store for Module<T: Trait> 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<IncludedBlocks<T>>;

		/// Attestations on a recent parachain block.
		pub ParaBlockAttestations: double_map T::BlockNumber, blake2_128(Hash) => Option<BlockAttestations<T>>;

		// Did we already have more attestations included in this block?
		DidUpdate: bool;
	}
}

decl_module! {
	/// Parachain-attestations module.
	pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
		/// Provide candidate receipts for parachains, in ascending order by id.
		fn more_attestations(origin, _more: MoreAttestations) -> Result {
			ensure_none(origin)?;
			ensure!(!<DidUpdate>::exists(), "More attestations can be added only once in a block.");
			<DidUpdate>::put(true);

			Ok(())
		}

		fn on_finalize(_n: T::BlockNumber) {
			<DidUpdate>::kill();
		}
	}
}

impl<T: Trait> Module<T> {
	/// Update recent candidates to contain the already-checked parachain candidates.
	pub(crate) fn note_included(heads: &[AttestedCandidate], para_blocks: IncludedBlocks<T>) {
		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) = <RecentParaBlocks<T>>::take(&mod_num) {
			<ParaBlockAttestations<T>>::remove_prefix(&old_entry.actual_number);
		}

		let validators = T::ValidatorIdentities::get();

		// make new entry.
		for (head, hash) in heads.iter().zip(&para_blocks.para_blocks) {
			let mut valid = Vec::new();
			let invalid = Vec::new();

			{
146
147
148
149
150
151
152
153
154
155
156
157
				let attesting_indices = head.validator_indices
					.iter()
					.enumerate()
					.filter(|(_, bit)| *bit)
					.inspect(|&(auth_index, _)| {
						if let Some(stash_id) = validators.get(auth_index) {
							valid.push(stash_id.clone());
						}
					})
					.map(|(i, _)| i as u32);

				T::RewardAttestation::reward_immediate(attesting_indices);
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
			}

			let summary = BlockAttestations {
				receipt: head.candidate().clone(),
				valid,
				invalid,
			};

			<ParaBlockAttestations<T>>::insert(&para_blocks.actual_number, hash, &summary);
		}

		<RecentParaBlocks<T>>::insert(&mod_num, &para_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<T: Trait> ProvideInherent for Module<T> {
	type Call = Call<T>;
Gavin Wood's avatar
Gavin Wood committed
181
	type Error = MakeFatalError<inherents::Error>;
182
183
184
185
186
187
188
189
	const INHERENT_IDENTIFIER: InherentIdentifier = MORE_ATTESTATIONS_IDENTIFIER;

	fn create_inherent(data: &InherentData) -> Option<Self::Call> {
		data.get_data::<InherentType>(&MORE_ATTESTATIONS_IDENTIFIER)
			.ok()
			.and_then(|x| x.map(Call::more_attestations))
	}
}