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