1#![cfg_attr(not(feature = "std"), no_std)]
21
22extern crate alloc;
23
24#[cfg(feature = "serde")]
25use serde::Serialize;
26
27use alloc::vec::Vec;
28use codec::{Codec, Decode, DecodeWithMemTracking, Encode};
29use scale_info::TypeInfo;
30#[cfg(feature = "std")]
31use sp_keystore::KeystorePtr;
32use sp_runtime::{
33 traits::{Header as HeaderT, NumberFor},
34 ConsensusEngineId, OpaqueValue, RuntimeDebug,
35};
36
37pub const CLIENT_LOG_TARGET: &str = "grandpa";
39pub const RUNTIME_LOG_TARGET: &str = "runtime::grandpa";
41
42pub const KEY_TYPE: sp_core::crypto::KeyTypeId = sp_application_crypto::key_types::GRANDPA;
44
45mod app {
46 use sp_application_crypto::{app_crypto, ed25519, key_types::GRANDPA};
47 app_crypto!(ed25519, GRANDPA);
48}
49
50sp_application_crypto::with_pair! {
51 pub type AuthorityPair = app::Pair;
53}
54
55pub type AuthorityId = app::Public;
57
58pub type AuthoritySignature = app::Signature;
60
61pub const GRANDPA_ENGINE_ID: ConsensusEngineId = *b"FRNK";
63
64pub type AuthorityWeight = u64;
66
67pub type AuthorityIndex = u64;
69
70pub type SetId = u64;
72
73pub type RoundNumber = u64;
75
76pub type AuthorityList = Vec<(AuthorityId, AuthorityWeight)>;
78
79pub type Message<Header> =
81 finality_grandpa::Message<<Header as HeaderT>::Hash, <Header as HeaderT>::Number>;
82
83pub type SignedMessage<Header> = finality_grandpa::SignedMessage<
85 <Header as HeaderT>::Hash,
86 <Header as HeaderT>::Number,
87 AuthoritySignature,
88 AuthorityId,
89>;
90
91pub type PrimaryPropose<Header> =
93 finality_grandpa::PrimaryPropose<<Header as HeaderT>::Hash, <Header as HeaderT>::Number>;
94pub type Prevote<Header> =
96 finality_grandpa::Prevote<<Header as HeaderT>::Hash, <Header as HeaderT>::Number>;
97pub type Precommit<Header> =
99 finality_grandpa::Precommit<<Header as HeaderT>::Hash, <Header as HeaderT>::Number>;
100pub type CatchUp<Header> = finality_grandpa::CatchUp<
102 <Header as HeaderT>::Hash,
103 <Header as HeaderT>::Number,
104 AuthoritySignature,
105 AuthorityId,
106>;
107pub type Commit<Header> = finality_grandpa::Commit<
109 <Header as HeaderT>::Hash,
110 <Header as HeaderT>::Number,
111 AuthoritySignature,
112 AuthorityId,
113>;
114
115pub type CompactCommit<Header> = finality_grandpa::CompactCommit<
117 <Header as HeaderT>::Hash,
118 <Header as HeaderT>::Number,
119 AuthoritySignature,
120 AuthorityId,
121>;
122
123#[derive(Clone, Encode, Decode, PartialEq, Eq, TypeInfo)]
132#[cfg_attr(feature = "std", derive(Debug))]
133pub struct GrandpaJustification<Header: HeaderT> {
134 pub round: u64,
135 pub commit: Commit<Header>,
136 pub votes_ancestries: Vec<Header>,
137}
138
139#[derive(Clone, Eq, PartialEq, Encode, Decode, RuntimeDebug, TypeInfo)]
141#[cfg_attr(feature = "serde", derive(Serialize))]
142pub struct ScheduledChange<N> {
143 pub next_authorities: AuthorityList,
145 pub delay: N,
147}
148
149#[derive(Decode, Encode, PartialEq, Eq, Clone, RuntimeDebug)]
151#[cfg_attr(feature = "serde", derive(Serialize))]
152pub enum ConsensusLog<N: Codec> {
153 #[codec(index = 1)]
166 ScheduledChange(ScheduledChange<N>),
167 #[codec(index = 2)]
182 ForcedChange(N, ScheduledChange<N>),
183 #[codec(index = 3)]
185 OnDisabled(AuthorityIndex),
186 #[codec(index = 4)]
189 Pause(N),
190 #[codec(index = 5)]
193 Resume(N),
194}
195
196impl<N: Codec> ConsensusLog<N> {
197 pub fn try_into_change(self) -> Option<ScheduledChange<N>> {
199 match self {
200 ConsensusLog::ScheduledChange(change) => Some(change),
201 _ => None,
202 }
203 }
204
205 pub fn try_into_forced_change(self) -> Option<(N, ScheduledChange<N>)> {
207 match self {
208 ConsensusLog::ForcedChange(median, change) => Some((median, change)),
209 _ => None,
210 }
211 }
212
213 pub fn try_into_pause(self) -> Option<N> {
215 match self {
216 ConsensusLog::Pause(delay) => Some(delay),
217 _ => None,
218 }
219 }
220
221 pub fn try_into_resume(self) -> Option<N> {
223 match self {
224 ConsensusLog::Resume(delay) => Some(delay),
225 _ => None,
226 }
227 }
228}
229
230#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, Eq, TypeInfo)]
235pub struct EquivocationProof<H, N> {
236 set_id: SetId,
237 equivocation: Equivocation<H, N>,
238}
239
240impl<H, N> EquivocationProof<H, N> {
241 pub fn new(set_id: SetId, equivocation: Equivocation<H, N>) -> Self {
244 EquivocationProof { set_id, equivocation }
245 }
246
247 pub fn set_id(&self) -> SetId {
249 self.set_id
250 }
251
252 pub fn round(&self) -> RoundNumber {
254 match self.equivocation {
255 Equivocation::Prevote(ref equivocation) => equivocation.round_number,
256 Equivocation::Precommit(ref equivocation) => equivocation.round_number,
257 }
258 }
259
260 pub fn offender(&self) -> &AuthorityId {
262 self.equivocation.offender()
263 }
264}
265
266#[derive(Clone, Debug, Decode, DecodeWithMemTracking, Encode, PartialEq, Eq, TypeInfo)]
269pub enum Equivocation<H, N> {
270 Prevote(
272 finality_grandpa::Equivocation<
273 AuthorityId,
274 finality_grandpa::Prevote<H, N>,
275 AuthoritySignature,
276 >,
277 ),
278 Precommit(
280 finality_grandpa::Equivocation<
281 AuthorityId,
282 finality_grandpa::Precommit<H, N>,
283 AuthoritySignature,
284 >,
285 ),
286}
287
288impl<H, N>
289 From<
290 finality_grandpa::Equivocation<
291 AuthorityId,
292 finality_grandpa::Prevote<H, N>,
293 AuthoritySignature,
294 >,
295 > for Equivocation<H, N>
296{
297 fn from(
298 equivocation: finality_grandpa::Equivocation<
299 AuthorityId,
300 finality_grandpa::Prevote<H, N>,
301 AuthoritySignature,
302 >,
303 ) -> Self {
304 Equivocation::Prevote(equivocation)
305 }
306}
307
308impl<H, N>
309 From<
310 finality_grandpa::Equivocation<
311 AuthorityId,
312 finality_grandpa::Precommit<H, N>,
313 AuthoritySignature,
314 >,
315 > for Equivocation<H, N>
316{
317 fn from(
318 equivocation: finality_grandpa::Equivocation<
319 AuthorityId,
320 finality_grandpa::Precommit<H, N>,
321 AuthoritySignature,
322 >,
323 ) -> Self {
324 Equivocation::Precommit(equivocation)
325 }
326}
327
328impl<H, N> Equivocation<H, N> {
329 pub fn offender(&self) -> &AuthorityId {
331 match self {
332 Equivocation::Prevote(ref equivocation) => &equivocation.identity,
333 Equivocation::Precommit(ref equivocation) => &equivocation.identity,
334 }
335 }
336
337 pub fn round_number(&self) -> RoundNumber {
339 match self {
340 Equivocation::Prevote(ref equivocation) => equivocation.round_number,
341 Equivocation::Precommit(ref equivocation) => equivocation.round_number,
342 }
343 }
344}
345
346pub fn check_equivocation_proof<H, N>(report: EquivocationProof<H, N>) -> bool
349where
350 H: Clone + Encode + PartialEq,
351 N: Clone + Encode + PartialEq,
352{
353 macro_rules! check {
356 ( $equivocation:expr, $message:expr ) => {
357 if $equivocation.first.0.target_hash == $equivocation.second.0.target_hash &&
359 $equivocation.first.0.target_number == $equivocation.second.0.target_number
360 {
361 return false
362 }
363
364 let valid_first = check_message_signature(
366 &$message($equivocation.first.0),
367 &$equivocation.identity,
368 &$equivocation.first.1,
369 $equivocation.round_number,
370 report.set_id,
371 )
372 .is_valid();
373
374 let valid_second = check_message_signature(
375 &$message($equivocation.second.0),
376 &$equivocation.identity,
377 &$equivocation.second.1,
378 $equivocation.round_number,
379 report.set_id,
380 )
381 .is_valid();
382
383 return valid_first && valid_second
384 };
385 }
386
387 match report.equivocation {
388 Equivocation::Prevote(equivocation) => {
389 check!(equivocation, finality_grandpa::Message::Prevote);
390 },
391 Equivocation::Precommit(equivocation) => {
392 check!(equivocation, finality_grandpa::Message::Precommit);
393 },
394 }
395}
396
397pub fn localized_payload<E: Encode>(round: RoundNumber, set_id: SetId, message: &E) -> Vec<u8> {
399 let mut buf = Vec::new();
400 localized_payload_with_buffer(round, set_id, message, &mut buf);
401 buf
402}
403
404pub fn localized_payload_with_buffer<E: Encode>(
408 round: RoundNumber,
409 set_id: SetId,
410 message: &E,
411 buf: &mut Vec<u8>,
412) {
413 buf.clear();
414 (message, round, set_id).encode_to(buf)
415}
416
417#[derive(Clone, Encode, Decode, PartialEq, Eq)]
419#[cfg_attr(feature = "std", derive(Debug))]
420pub enum SignatureResult {
421 Valid,
423
424 Invalid,
426
427 OutdatedSet,
429}
430
431impl SignatureResult {
432 pub fn is_valid(&self) -> bool {
434 matches!(self, SignatureResult::Valid)
435 }
436}
437
438pub fn check_message_signature<H, N>(
441 message: &finality_grandpa::Message<H, N>,
442 id: &AuthorityId,
443 signature: &AuthoritySignature,
444 round: RoundNumber,
445 set_id: SetId,
446) -> SignatureResult
447where
448 H: Encode,
449 N: Encode,
450{
451 check_message_signature_with_buffer(message, id, signature, round, set_id, &mut Vec::new())
452}
453
454pub fn check_message_signature_with_buffer<H, N>(
459 message: &finality_grandpa::Message<H, N>,
460 id: &AuthorityId,
461 signature: &AuthoritySignature,
462 round: RoundNumber,
463 set_id: SetId,
464 buf: &mut Vec<u8>,
465) -> SignatureResult
466where
467 H: Encode,
468 N: Encode,
469{
470 use sp_application_crypto::RuntimeAppPublic;
471
472 localized_payload_with_buffer(round, set_id, message, buf);
473
474 if id.verify(&buf, signature) {
475 return SignatureResult::Valid;
476 }
477
478 let log_target = if cfg!(feature = "std") { CLIENT_LOG_TARGET } else { RUNTIME_LOG_TARGET };
479 log::debug!(
480 target: log_target,
481 "Bad signature on message from id={id:?} round={round:?} set_id={set_id:?}",
482 );
483
484 if set_id == 0 {
486 return SignatureResult::Invalid;
487 }
488
489 let prev_set_id = set_id - 1;
490 localized_payload_with_buffer(round, prev_set_id, message, buf);
491 let valid = id.verify(&buf, signature);
492 log::debug!(
493 target: log_target,
494 "Previous set signature check for id={id:?} round={round:?} previous_set={prev_set_id:?} valid={valid:?}"
495 );
496
497 if valid {
498 SignatureResult::OutdatedSet
499 } else {
500 SignatureResult::Invalid
501 }
502}
503
504#[cfg(feature = "std")]
506pub fn sign_message<H, N>(
507 keystore: KeystorePtr,
508 message: finality_grandpa::Message<H, N>,
509 public: AuthorityId,
510 round: RoundNumber,
511 set_id: SetId,
512) -> Option<finality_grandpa::SignedMessage<H, N, AuthoritySignature, AuthorityId>>
513where
514 H: Encode,
515 N: Encode,
516{
517 use sp_application_crypto::AppCrypto;
518
519 let encoded = localized_payload(round, set_id, &message);
520 let signature = keystore
521 .ed25519_sign(AuthorityId::ID, public.as_ref(), &encoded[..])
522 .ok()
523 .flatten()?
524 .try_into()
525 .ok()?;
526
527 Some(finality_grandpa::SignedMessage { message, signature, id: public })
528}
529
530pub type OpaqueKeyOwnershipProof = OpaqueValue;
537
538sp_api::decl_runtime_apis! {
539 #[api_version(3)]
549 pub trait GrandpaApi {
550 fn grandpa_authorities() -> AuthorityList;
557
558 fn submit_report_equivocation_unsigned_extrinsic(
567 equivocation_proof: EquivocationProof<Block::Hash, NumberFor<Block>>,
568 key_owner_proof: OpaqueKeyOwnershipProof,
569 ) -> Option<()>;
570
571 fn generate_key_ownership_proof(
583 set_id: SetId,
584 authority_id: AuthorityId,
585 ) -> Option<OpaqueKeyOwnershipProof>;
586
587 fn current_set_id() -> SetId;
589 }
590}