referrerpolicy=no-referrer-when-downgrade

bp_header_chain/justification/
mod.rs

1// Copyright (C) Parity Technologies (UK) Ltd.
2// This file is part of Parity Bridges Common.
3
4// Parity Bridges Common is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// Parity Bridges Common is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.
16
17//! Logic for checking GRANDPA Finality Proofs.
18//!
19//! Adapted copy of substrate/client/finality-grandpa/src/justification.rs. If origin
20//! will ever be moved to the sp_consensus_grandpa, we should reuse that implementation.
21
22mod verification;
23
24use crate::ChainWithGrandpa;
25pub use verification::{
26	equivocation::{EquivocationsCollector, GrandpaEquivocationsFinder},
27	optimizer::verify_and_optimize_justification,
28	strict::verify_justification,
29	AncestryChain, Error as JustificationVerificationError, JustificationVerificationContext,
30	PrecommitError,
31};
32
33use bp_runtime::{BlockNumberOf, Chain, HashOf, HeaderId};
34use codec::{Decode, DecodeWithMemTracking, Encode, MaxEncodedLen};
35use scale_info::TypeInfo;
36use sp_consensus_grandpa::{AuthorityId, AuthoritySignature};
37use sp_runtime::{traits::Header as HeaderT, RuntimeDebug, SaturatedConversion};
38use sp_std::prelude::*;
39
40/// A GRANDPA Justification is a proof that a given header was finalized
41/// at a certain height and with a certain set of authorities.
42///
43/// This particular proof is used to prove that headers on a bridged chain
44/// (so not our chain) have been finalized correctly.
45#[derive(Encode, Decode, DecodeWithMemTracking, Clone, PartialEq, Eq, TypeInfo)]
46#[cfg_attr(feature = "std", derive(Debug))]
47pub struct GrandpaJustification<Header: HeaderT> {
48	/// The round (voting period) this justification is valid for.
49	pub round: u64,
50	/// The set of votes for the chain which is to be finalized.
51	pub commit:
52		finality_grandpa::Commit<Header::Hash, Header::Number, AuthoritySignature, AuthorityId>,
53	/// A proof that the chain of blocks in the commit are related to each other.
54	pub votes_ancestries: Vec<Header>,
55}
56
57// A proper Debug impl for no-std is not possible for the `GrandpaJustification` since the `Commit`
58// type only implements Debug that for `std` here:
59// https://github.com/paritytech/finality-grandpa/blob/8c45a664c05657f0c71057158d3ba555ba7d20de/src/lib.rs#L275
60// so we do a manual impl.
61#[cfg(not(feature = "std"))]
62impl<H: HeaderT> core::fmt::Debug for GrandpaJustification<H> {
63	fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
64		write!(f, "GrandpaJustification {{ round: {:?}, commit: <wasm:stripped>, votes_ancestries: {:?} }}", self.round, self.votes_ancestries)
65	}
66}
67
68impl<H: HeaderT> GrandpaJustification<H> {
69	/// Returns reasonable size of justification using constants from the provided chain.
70	///
71	/// An imprecise analogue of `MaxEncodedLen` implementation. We don't use it for
72	/// any precise calculations - that's just an estimation.
73	pub fn max_reasonable_size<C>(required_precommits: u32) -> u32
74	where
75		C: Chain + ChainWithGrandpa,
76	{
77		// we don't need precise results here - just estimations, so some details
78		// are removed from computations (e.g. bytes required to encode vector length)
79
80		// structures in `finality_grandpa` crate are not implementing `MaxEncodedLength`, so
81		// here's our estimation for the `finality_grandpa::Commit` struct size
82		//
83		// precommit is: hash + number
84		// signed precommit is: precommit + signature (64b) + authority id
85		// commit is: hash + number + vec of signed precommits
86		let signed_precommit_size: u32 = BlockNumberOf::<C>::max_encoded_len()
87			.saturating_add(HashOf::<C>::max_encoded_len().saturated_into())
88			.saturating_add(64)
89			.saturating_add(AuthorityId::max_encoded_len().saturated_into())
90			.saturated_into();
91		let max_expected_signed_commit_size = signed_precommit_size
92			.saturating_mul(required_precommits)
93			.saturating_add(BlockNumberOf::<C>::max_encoded_len().saturated_into())
94			.saturating_add(HashOf::<C>::max_encoded_len().saturated_into());
95
96		let max_expected_votes_ancestries_size =
97			C::REASONABLE_HEADERS_IN_JUSTIFICATION_ANCESTRY.saturating_mul(C::AVERAGE_HEADER_SIZE);
98
99		// justification is round number (u64=8b), a signed GRANDPA commit and the
100		// `votes_ancestries` vector
101		8u32.saturating_add(max_expected_signed_commit_size)
102			.saturating_add(max_expected_votes_ancestries_size)
103	}
104
105	/// Return identifier of header that this justification claims to finalize.
106	pub fn commit_target_id(&self) -> HeaderId<H::Hash, H::Number> {
107		HeaderId(self.commit.target_number, self.commit.target_hash)
108	}
109}
110
111impl<H: HeaderT> crate::FinalityProof<H::Hash, H::Number> for GrandpaJustification<H> {
112	fn target_header_hash(&self) -> H::Hash {
113		self.commit.target_hash
114	}
115
116	fn target_header_number(&self) -> H::Number {
117		self.commit.target_number
118	}
119}
120
121/// Justification verification error.
122#[derive(Eq, RuntimeDebug, PartialEq)]
123pub enum Error {
124	/// Failed to decode justification.
125	JustificationDecode,
126}
127
128/// Given GRANDPA authorities set size, return number of valid authorities votes that the
129/// justification must have to be valid.
130///
131/// This function assumes that all authorities have the same vote weight.
132pub fn required_justification_precommits(authorities_set_length: u32) -> u32 {
133	authorities_set_length - authorities_set_length.saturating_sub(1) / 3
134}
135
136/// Decode justification target.
137pub fn decode_justification_target<Header: HeaderT>(
138	raw_justification: &[u8],
139) -> Result<(Header::Hash, Header::Number), Error> {
140	GrandpaJustification::<Header>::decode(&mut &*raw_justification)
141		.map(|justification| (justification.commit.target_hash, justification.commit.target_number))
142		.map_err(|_| Error::JustificationDecode)
143}