polkadot_node_primitives/disputes/
message.rs1use thiserror::Error;
23
24use codec::{Decode, Encode};
25
26use super::{InvalidDisputeVote, SignedDisputeStatement, ValidDisputeVote};
27use polkadot_primitives::{
28 CandidateReceiptV2 as CandidateReceipt, DisputeStatement, SessionIndex, SessionInfo,
29 ValidatorIndex,
30};
31
32#[derive(Debug, Clone)]
41pub struct DisputeMessage(UncheckedDisputeMessage);
42
43#[derive(Clone, Encode, Decode, Debug)]
45pub struct UncheckedDisputeMessage {
46 pub candidate_receipt: CandidateReceipt,
48
49 pub session_index: SessionIndex,
51
52 pub invalid_vote: InvalidDisputeVote,
54
55 pub valid_vote: ValidDisputeVote,
57}
58
59#[derive(Error, Debug)]
61pub enum Error {
62 #[error("Candidate hashes of the two votes did not match up")]
64 CandidateHashMismatch,
65
66 #[error("Session indices of the two votes did not match up")]
68 SessionIndexMismatch,
69
70 #[error("Valid statement validator key did not match session information")]
72 InvalidValidKey,
73
74 #[error("Invalid statement validator key did not match session information")]
76 InvalidInvalidKey,
77
78 #[error("Hash of candidate receipt did not match provided hash")]
80 InvalidCandidateReceipt,
81
82 #[error("Valid statement has kind `invalid`")]
84 ValidStatementHasInvalidKind,
85
86 #[error("Invalid statement has kind `valid`")]
88 InvalidStatementHasValidKind,
89
90 #[error("The valid statement had an invalid validator index")]
92 ValidStatementInvalidValidatorIndex,
93
94 #[error("The invalid statement had an invalid validator index")]
96 InvalidStatementInvalidValidatorIndex,
97}
98
99impl DisputeMessage {
100 pub fn from_signed_statements(
122 valid_statement: SignedDisputeStatement,
123 valid_index: ValidatorIndex,
124 invalid_statement: SignedDisputeStatement,
125 invalid_index: ValidatorIndex,
126 candidate_receipt: CandidateReceipt,
127 session_info: &SessionInfo,
128 ) -> Result<Self, Error> {
129 let candidate_hash = *valid_statement.candidate_hash();
130 if candidate_hash != *invalid_statement.candidate_hash() {
132 return Err(Error::CandidateHashMismatch)
133 }
134
135 let session_index = valid_statement.session_index();
136 if session_index != invalid_statement.session_index() {
137 return Err(Error::SessionIndexMismatch)
138 }
139
140 let valid_id = session_info
141 .validators
142 .get(valid_index)
143 .ok_or(Error::ValidStatementInvalidValidatorIndex)?;
144 let invalid_id = session_info
145 .validators
146 .get(invalid_index)
147 .ok_or(Error::InvalidStatementInvalidValidatorIndex)?;
148
149 if valid_id != valid_statement.validator_public() {
150 return Err(Error::InvalidValidKey)
151 }
152
153 if invalid_id != invalid_statement.validator_public() {
154 return Err(Error::InvalidInvalidKey)
155 }
156
157 if candidate_receipt.hash() != candidate_hash {
158 return Err(Error::InvalidCandidateReceipt)
159 }
160
161 let valid_kind = match valid_statement.statement() {
162 DisputeStatement::Valid(v) => v,
163 _ => return Err(Error::ValidStatementHasInvalidKind),
164 };
165
166 let invalid_kind = match invalid_statement.statement() {
167 DisputeStatement::Invalid(v) => v,
168 _ => return Err(Error::InvalidStatementHasValidKind),
169 };
170
171 let valid_vote = ValidDisputeVote {
172 validator_index: valid_index,
173 signature: valid_statement.validator_signature().clone(),
174 kind: valid_kind.clone(),
175 };
176
177 let invalid_vote = InvalidDisputeVote {
178 validator_index: invalid_index,
179 signature: invalid_statement.validator_signature().clone(),
180 kind: *invalid_kind,
181 };
182
183 Ok(DisputeMessage(UncheckedDisputeMessage {
184 candidate_receipt,
185 session_index,
186 valid_vote,
187 invalid_vote,
188 }))
189 }
190
191 pub fn candidate_receipt(&self) -> &CandidateReceipt {
193 &self.0.candidate_receipt
194 }
195
196 pub fn session_index(&self) -> SessionIndex {
198 self.0.session_index
199 }
200
201 pub fn invalid_vote(&self) -> &InvalidDisputeVote {
203 &self.0.invalid_vote
204 }
205
206 pub fn valid_vote(&self) -> &ValidDisputeVote {
208 &self.0.valid_vote
209 }
210}
211
212impl UncheckedDisputeMessage {
213 pub fn try_into_signed_votes(
215 self,
216 session_info: &SessionInfo,
217 ) -> Result<
218 (
219 CandidateReceipt,
220 (SignedDisputeStatement, ValidatorIndex),
221 (SignedDisputeStatement, ValidatorIndex),
222 ),
223 (),
224 > {
225 let Self { candidate_receipt, session_index, valid_vote, invalid_vote } = self;
226 let candidate_hash = candidate_receipt.hash();
227
228 let vote_valid = {
229 let ValidDisputeVote { validator_index, signature, kind } = valid_vote;
230 let validator_public = session_info.validators.get(validator_index).ok_or(())?.clone();
231
232 (
233 SignedDisputeStatement::new_checked(
234 DisputeStatement::Valid(kind),
235 candidate_hash,
236 session_index,
237 validator_public,
238 signature,
239 )?,
240 validator_index,
241 )
242 };
243
244 let vote_invalid = {
245 let InvalidDisputeVote { validator_index, signature, kind } = invalid_vote;
246 let validator_public = session_info.validators.get(validator_index).ok_or(())?.clone();
247
248 (
249 SignedDisputeStatement::new_checked(
250 DisputeStatement::Invalid(kind),
251 candidate_hash,
252 session_index,
253 validator_public,
254 signature,
255 )?,
256 validator_index,
257 )
258 };
259
260 Ok((candidate_receipt, vote_valid, vote_invalid))
261 }
262}
263
264impl From<DisputeMessage> for UncheckedDisputeMessage {
265 fn from(message: DisputeMessage) -> Self {
266 message.0
267 }
268}