attestations.rs 6.89 KB
Newer Older
Shawn Tabrizi's avatar
Shawn Tabrizi committed
1
// Copyright 2019-2020 Parity Technologies (UK) Ltd.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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.

22
use sp_std::prelude::*;
23
use codec::{Encode, Decode};
24
25
26
use frame_support::{
	decl_storage, decl_module, decl_error, ensure, dispatch::DispatchResult, traits::Get
};
27

28
use primitives::{Hash, parachain::{AttestedCandidate, AbridgedCandidateReceipt, Id as ParaId}};
29
30
use sp_runtime::RuntimeDebug;
use sp_staking::SessionIndex;
31

Gavin Wood's avatar
Gavin Wood committed
32
use inherents::{ProvideInherent, InherentData, MakeFatalError, InherentIdentifier};
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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> {
53
	receipt: AbridgedCandidateReceipt,
54
55
56
57
58
	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.
59
#[derive(Encode, Decode, Clone, PartialEq, RuntimeDebug)]
60
61
pub struct MoreAttestations;

62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
/// 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>) {
Gavin Wood's avatar
Gavin Wood committed
78
79
		use staking::SessionInterface;

80
81
82
83
		// 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;

Gavin Wood's avatar
Gavin Wood committed
84
85
86
87
88
89
90
		let validators = T::SessionInterface::validators();

		let validator_rewards = validator_indices.into_iter()
			.filter_map(|i| validators.get(i as usize).cloned())
			.map(|v| (v, STAKING_REWARD_POINTS));

		Self::reward_by_ids(validator_rewards);
91
92
93
	}
}

94
95
96
97
98
99
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>>;
100
101
102

	/// Hook for rewarding validators upon attesting.
	type RewardAttestation: RewardAttestation;
103
104
105
106
107
108
}

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.
109
		pub RecentParaBlocks: map hasher(twox_64_concat) T::BlockNumber => Option<IncludedBlocks<T>>;
110
111

		/// Attestations on a recent parachain block.
Gavin Wood's avatar
Gavin Wood committed
112
		pub ParaBlockAttestations:
113
			double_map hasher(twox_64_concat) T::BlockNumber, hasher(identity) Hash
Gavin Wood's avatar
Gavin Wood committed
114
			=> Option<BlockAttestations<T>>;
115
116
117
118
119
120

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

121
122
123
124
125
126
127
decl_error! {
	pub enum Error for Module<T: Trait> {
		/// More attestations can be added only once in a block.
		TooManyAttestations,
	}
}

128
129
130
decl_module! {
	/// Parachain-attestations module.
	pub struct Module<T: Trait> for enum Call where origin: <T as system::Trait>::Origin {
131
132
		type Error = Error<T>;

133
		/// Provide candidate receipts for parachains, in ascending order by id.
Kian Paimani's avatar
Kian Paimani committed
134
		#[weight = frame_support::weights::SimpleDispatchInfo::default()]
135
		fn more_attestations(origin, _more: MoreAttestations) -> DispatchResult {
136
			ensure_none(origin)?;
137
138
			ensure!(!DidUpdate::exists(), Error::<T>::TooManyAttestations);
			DidUpdate::put(true);
139
140
141
142
143

			Ok(())
		}

		fn on_finalize(_n: T::BlockNumber) {
144
			DidUpdate::kill();
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
		}
	}
}

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();

			{
168
169
170
				let attesting_indices = head.validator_indices
					.iter()
					.enumerate()
171
					.filter(|(_, bit)| **bit)
172
173
174
175
176
177
178
179
					.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);
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
			}

			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
203
	type Error = MakeFatalError<inherents::Error>;
204
205
206
207
208
209
210
211
	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))
	}
}