1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// Copyright (C) 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/>.

//! Assignment criteria VRF generation and checking interfaces.

use crate::approval::{
	v1::{DelayTranche, RelayVRFStory},
	v2::{AssignmentCertV2, CoreBitfield},
};
use codec::{Decode, Encode};
use polkadot_primitives::{
	AssignmentId, CandidateHash, CoreIndex, GroupIndex, IndexedVec, SessionInfo, ValidatorIndex,
};
use sc_keystore::LocalKeystore;

use std::collections::HashMap;

/// Details pertaining to our assignment on a block.
#[derive(Debug, Clone, Encode, Decode, PartialEq)]
pub struct OurAssignment {
	cert: AssignmentCertV2,
	tranche: DelayTranche,
	validator_index: ValidatorIndex,
	// Whether the assignment has been triggered already.
	triggered: bool,
}

impl OurAssignment {
	/// Create a new `OurAssignment`.
	pub fn new(
		cert: AssignmentCertV2,
		tranche: DelayTranche,
		validator_index: ValidatorIndex,
		triggered: bool,
	) -> Self {
		OurAssignment { cert, tranche, validator_index, triggered }
	}
	/// Returns a reference to the assignment cert.
	pub fn cert(&self) -> &AssignmentCertV2 {
		&self.cert
	}

	/// Returns the assignment cert.
	pub fn into_cert(self) -> AssignmentCertV2 {
		self.cert
	}

	/// Returns the delay tranche of the assignment.
	pub fn tranche(&self) -> DelayTranche {
		self.tranche
	}

	/// Returns the validator index of the assignment.
	pub fn validator_index(&self) -> ValidatorIndex {
		self.validator_index
	}

	/// Returns whether the assignment has been triggered.
	pub fn triggered(&self) -> bool {
		self.triggered
	}

	/// Marks the assignment as triggered.
	pub fn mark_triggered(&mut self) {
		self.triggered = true;
	}
}

/// Information about the world assignments are being produced in.
#[derive(Clone, Debug)]
pub struct Config {
	/// The assignment public keys for validators.
	pub assignment_keys: Vec<AssignmentId>,
	/// The groups of validators assigned to each core.
	pub validator_groups: IndexedVec<GroupIndex, Vec<ValidatorIndex>>,
	/// The number of availability cores used by the protocol during this session.
	pub n_cores: u32,
	/// The zeroth delay tranche width.
	pub zeroth_delay_tranche_width: u32,
	/// The number of samples we do of `relay_vrf_modulo`.
	pub relay_vrf_modulo_samples: u32,
	/// The number of delay tranches in total.
	pub n_delay_tranches: u32,
}

impl<'a> From<&'a SessionInfo> for Config {
	fn from(s: &'a SessionInfo) -> Self {
		Config {
			assignment_keys: s.assignment_keys.clone(),
			validator_groups: s.validator_groups.clone(),
			n_cores: s.n_cores,
			zeroth_delay_tranche_width: s.zeroth_delay_tranche_width,
			relay_vrf_modulo_samples: s.relay_vrf_modulo_samples,
			n_delay_tranches: s.n_delay_tranches,
		}
	}
}

/// A trait for producing and checking assignments.
///
/// Approval voting subsystem implements a a real implemention
/// for it and tests use a mock implementation.
pub trait AssignmentCriteria {
	/// Compute the assignments for the given relay VRF story.
	fn compute_assignments(
		&self,
		keystore: &LocalKeystore,
		relay_vrf_story: RelayVRFStory,
		config: &Config,
		leaving_cores: Vec<(CandidateHash, CoreIndex, GroupIndex)>,
		enable_v2_assignments: bool,
	) -> HashMap<CoreIndex, OurAssignment>;

	/// Check the assignment cert for the given relay VRF story and returns the delay tranche.
	fn check_assignment_cert(
		&self,
		claimed_core_bitfield: CoreBitfield,
		validator_index: ValidatorIndex,
		config: &Config,
		relay_vrf_story: RelayVRFStory,
		assignment: &AssignmentCertV2,
		// Backing groups for each "leaving core".
		backing_groups: Vec<GroupIndex>,
	) -> Result<DelayTranche, InvalidAssignment>;
}

/// Assignment invalid.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct InvalidAssignment(pub InvalidAssignmentReason);

impl std::fmt::Display for InvalidAssignment {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "Invalid Assignment: {:?}", self.0)
	}
}

impl std::error::Error for InvalidAssignment {}

/// Failure conditions when checking an assignment cert.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum InvalidAssignmentReason {
	/// The validator index is out of bounds.
	ValidatorIndexOutOfBounds,
	/// Sample index is out of bounds.
	SampleOutOfBounds,
	/// Core index is out of bounds.
	CoreIndexOutOfBounds,
	/// Invalid assignment key.
	InvalidAssignmentKey,
	/// Node is in backing group.
	IsInBackingGroup,
	/// Modulo core index mismatch.
	VRFModuloCoreIndexMismatch,
	/// Modulo output mismatch.
	VRFModuloOutputMismatch,
	/// Delay core index mismatch.
	VRFDelayCoreIndexMismatch,
	/// Delay output mismatch.
	VRFDelayOutputMismatch,
	/// Invalid arguments
	InvalidArguments,
	/// Assignment vrf check resulted in 0 assigned cores.
	NullAssignment,
}