referrerpolicy=no-referrer-when-downgrade

sc_consensus_grandpa/
finality_proof.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! GRANDPA block finality proof generation and check.
20//!
21//! Finality of block B is proved by providing:
22//! 1) the justification for the descendant block F;
23//! 2) headers sub-chain (B; F] if B != F;
24//! 3) proof of GRANDPA::authorities() if the set changes at block F.
25//!
26//! Since earliest possible justification is returned, the GRANDPA authorities set
27//! at the block F is guaranteed to be the same as in the block B (this is because block
28//! that enacts new GRANDPA authorities set always comes with justification). It also
29//! means that the `set_id` is the same at blocks B and F.
30//!
31//! Let U be the last finalized block known to caller. If authorities set has changed several
32//! times in the (U; F] interval, multiple finality proof fragments are returned (one for each
33//! authority set change) and they must be verified in-order.
34//!
35//! Finality proof provider can choose how to provide finality proof on its own. The incomplete
36//! finality proof (that finalizes some block C that is ancestor of the B and descendant
37//! of the U) could be returned.
38
39use log::{trace, warn};
40use std::sync::Arc;
41
42use codec::{Decode, Encode};
43use sc_client_api::backend::Backend;
44use sp_blockchain::{Backend as BlockchainBackend, HeaderBackend};
45use sp_consensus_grandpa::GRANDPA_ENGINE_ID;
46use sp_runtime::{
47	generic::BlockId,
48	traits::{Block as BlockT, Header as HeaderT, NumberFor, One},
49};
50
51use crate::{
52	authorities::{AuthoritySetChangeId, AuthoritySetChanges},
53	best_justification,
54	justification::GrandpaJustification,
55	SharedAuthoritySet, LOG_TARGET,
56};
57
58const MAX_UNKNOWN_HEADERS: usize = 100_000;
59
60/// Finality proof provider for serving network requests.
61#[derive(Clone)]
62pub struct FinalityProofProvider<BE, Block: BlockT> {
63	backend: Arc<BE>,
64	shared_authority_set: Option<SharedAuthoritySet<Block::Hash, NumberFor<Block>>>,
65}
66
67impl<B, Block> FinalityProofProvider<B, Block>
68where
69	Block: BlockT,
70	B: Backend<Block>,
71{
72	/// Create new finality proof provider using:
73	///
74	/// - backend for accessing blockchain data;
75	/// - authority_provider for calling and proving runtime methods.
76	/// - shared_authority_set for accessing authority set data
77	pub fn new(
78		backend: Arc<B>,
79		shared_authority_set: Option<SharedAuthoritySet<Block::Hash, NumberFor<Block>>>,
80	) -> Self {
81		FinalityProofProvider { backend, shared_authority_set }
82	}
83
84	/// Create new finality proof provider for the service using:
85	///
86	/// - backend for accessing blockchain data;
87	/// - storage_provider, which is generally a client.
88	/// - shared_authority_set for accessing authority set data
89	pub fn new_for_service(
90		backend: Arc<B>,
91		shared_authority_set: Option<SharedAuthoritySet<Block::Hash, NumberFor<Block>>>,
92	) -> Arc<Self> {
93		Arc::new(Self::new(backend, shared_authority_set))
94	}
95}
96
97impl<B, Block> FinalityProofProvider<B, Block>
98where
99	Block: BlockT,
100	B: Backend<Block>,
101{
102	/// Prove finality for the given block number by returning a Justification for the last block of
103	/// the authority set in bytes.
104	pub fn prove_finality(
105		&self,
106		block: NumberFor<Block>,
107	) -> Result<Option<Vec<u8>>, FinalityProofError> {
108		Ok(self.prove_finality_proof(block, true)?.map(|proof| proof.encode()))
109	}
110
111	/// Prove finality for the given block number by returning a Justification for the last block of
112	/// the authority set.
113	///
114	/// If `collect_unknown_headers` is true, the finality proof will include all headers from the
115	/// requested block until the block the justification refers to.
116	pub fn prove_finality_proof(
117		&self,
118		block: NumberFor<Block>,
119		collect_unknown_headers: bool,
120	) -> Result<Option<FinalityProof<Block::Header>>, FinalityProofError> {
121		let authority_set_changes = if let Some(changes) = self
122			.shared_authority_set
123			.as_ref()
124			.map(SharedAuthoritySet::authority_set_changes)
125		{
126			changes
127		} else {
128			return Ok(None)
129		};
130
131		prove_finality(&*self.backend, authority_set_changes, block, collect_unknown_headers)
132	}
133}
134
135/// Finality for block B is proved by providing:
136/// 1) the justification for the descendant block F;
137/// 2) headers sub-chain (B; F] if B != F;
138#[derive(Debug, PartialEq, Encode, Decode, Clone)]
139pub struct FinalityProof<Header: HeaderT> {
140	/// The hash of block F for which justification is provided.
141	pub block: Header::Hash,
142	/// Justification of the block F.
143	pub justification: Vec<u8>,
144	/// The set of headers in the range (B; F] that we believe are unknown to the caller. Ordered.
145	pub unknown_headers: Vec<Header>,
146}
147
148/// Errors occurring when trying to prove finality
149#[derive(Debug, thiserror::Error)]
150pub enum FinalityProofError {
151	/// The requested block has not yet been finalized.
152	#[error("Block not yet finalized")]
153	BlockNotYetFinalized,
154	/// The requested block is not covered by authority set changes. Likely this means the block is
155	/// in the latest authority set, and the subscription API is more appropriate.
156	#[error("Block not covered by authority set changes")]
157	BlockNotInAuthoritySetChanges,
158	/// Errors originating from the client.
159	#[error(transparent)]
160	Client(#[from] sp_blockchain::Error),
161}
162
163/// Prove finality for the given block number by returning a justification for the last block of
164/// the authority set of which the given block is part of, or a justification for the latest
165/// finalized block if the given block is part of the current authority set.
166///
167/// If `collect_unknown_headers` is true, the finality proof will include all headers from the
168/// requested block until the block the justification refers to.
169fn prove_finality<Block, B>(
170	backend: &B,
171	authority_set_changes: AuthoritySetChanges<NumberFor<Block>>,
172	block: NumberFor<Block>,
173	collect_unknown_headers: bool,
174) -> Result<Option<FinalityProof<Block::Header>>, FinalityProofError>
175where
176	Block: BlockT,
177	B: Backend<Block>,
178{
179	// Early-return if we are sure that there are no blocks finalized that cover the requested
180	// block.
181	let finalized_number = backend.blockchain().info().finalized_number;
182	if finalized_number < block {
183		let err = format!(
184			"Requested finality proof for descendant of #{} while we only have finalized #{}.",
185			block, finalized_number,
186		);
187		trace!(target: LOG_TARGET, "{}", &err);
188		return Err(FinalityProofError::BlockNotYetFinalized)
189	}
190
191	let (justification, just_block) = match authority_set_changes.get_set_id(block) {
192		AuthoritySetChangeId::Latest => {
193			if let Some(justification) = best_justification(backend)?
194				.map(|j: GrandpaJustification<Block>| (j.encode(), j.target().0))
195			{
196				justification
197			} else {
198				trace!(
199					target: LOG_TARGET,
200					"No justification found for the latest finalized block. \
201					Returning empty proof.",
202				);
203				return Ok(None)
204			}
205		},
206		AuthoritySetChangeId::Set(_, last_block_for_set) => {
207			let last_block_for_set_id = backend
208				.blockchain()
209				.expect_block_hash_from_id(&BlockId::Number(last_block_for_set))?;
210			let justification = if let Some(grandpa_justification) = backend
211				.blockchain()
212				.justifications(last_block_for_set_id)?
213				.and_then(|justifications| justifications.into_justification(GRANDPA_ENGINE_ID))
214			{
215				grandpa_justification
216			} else {
217				trace!(
218					target: LOG_TARGET,
219					"No justification found when making finality proof for {}. \
220					Returning empty proof.",
221					block,
222				);
223				return Ok(None)
224			};
225			(justification, last_block_for_set)
226		},
227		AuthoritySetChangeId::Unknown => {
228			warn!(
229				target: LOG_TARGET,
230				"AuthoritySetChanges does not cover the requested block #{} due to missing data. \
231				 You need to resync to populate AuthoritySetChanges properly.",
232				block,
233			);
234			return Err(FinalityProofError::BlockNotInAuthoritySetChanges)
235		},
236	};
237
238	let mut headers = Vec::new();
239	if collect_unknown_headers {
240		// Collect all headers from the requested block until the last block of the set
241		let mut current = block + One::one();
242		loop {
243			if current > just_block || headers.len() >= MAX_UNKNOWN_HEADERS {
244				break
245			}
246			let hash = backend.blockchain().expect_block_hash_from_id(&BlockId::Number(current))?;
247			headers.push(backend.blockchain().expect_header(hash)?);
248			current += One::one();
249		}
250	};
251
252	Ok(Some(FinalityProof {
253		block: backend.blockchain().expect_block_hash_from_id(&BlockId::Number(just_block))?,
254		justification,
255		unknown_headers: headers,
256	}))
257}
258
259#[cfg(test)]
260mod tests {
261	use super::*;
262	use crate::{authorities::AuthoritySetChanges, BlockNumberOps, ClientError, SetId};
263	use futures::executor::block_on;
264	use sc_block_builder::BlockBuilderBuilder;
265	use sc_client_api::{apply_aux, LockImportRun};
266	use sp_consensus::BlockOrigin;
267	use sp_consensus_grandpa::GRANDPA_ENGINE_ID as ID;
268	use sp_core::crypto::UncheckedFrom;
269	use sp_keyring::Ed25519Keyring;
270	use substrate_test_runtime_client::{
271		runtime::{Block, Header, H256},
272		Backend as TestBackend, ClientBlockImportExt, ClientExt, DefaultTestClientBuilderExt,
273		TestClient, TestClientBuilder, TestClientBuilderExt,
274	};
275
276	/// Check GRANDPA proof-of-finality for the given block.
277	///
278	/// Returns the vector of headers that MUST be validated + imported
279	/// AND if at least one of those headers is invalid, all other MUST be considered invalid.
280	fn check_finality_proof<Block: BlockT>(
281		current_set_id: SetId,
282		current_authorities: sp_consensus_grandpa::AuthorityList,
283		remote_proof: Vec<u8>,
284	) -> sp_blockchain::Result<super::FinalityProof<Block::Header>>
285	where
286		NumberFor<Block>: BlockNumberOps,
287	{
288		let proof = super::FinalityProof::<Block::Header>::decode(&mut &remote_proof[..])
289			.map_err(|_| ClientError::BadJustification("failed to decode finality proof".into()))?;
290
291		let justification: GrandpaJustification<Block> =
292			Decode::decode(&mut &proof.justification[..])
293				.map_err(|_| ClientError::JustificationDecode)?;
294
295		justification.verify(current_set_id, &current_authorities)?;
296
297		Ok(proof)
298	}
299
300	pub(crate) type FinalityProof = super::FinalityProof<Header>;
301
302	fn header(number: u64) -> Header {
303		let parent_hash = match number {
304			0 => Default::default(),
305			_ => header(number - 1).hash(),
306		};
307		Header::new(
308			number,
309			H256::from_low_u64_be(0),
310			H256::from_low_u64_be(0),
311			parent_hash,
312			Default::default(),
313		)
314	}
315
316	fn test_blockchain(
317		number_of_blocks: u64,
318		to_finalize: &[u64],
319	) -> (Arc<TestClient>, Arc<TestBackend>, Vec<Block>) {
320		let builder = TestClientBuilder::new();
321		let backend = builder.backend();
322		let client = Arc::new(builder.build());
323
324		let mut blocks = Vec::new();
325		for _ in 0..number_of_blocks {
326			let block = BlockBuilderBuilder::new(&*client)
327				.on_parent_block(client.chain_info().best_hash)
328				.with_parent_block_number(client.chain_info().best_number)
329				.build()
330				.unwrap()
331				.build()
332				.unwrap()
333				.block;
334			block_on(client.import(BlockOrigin::Own, block.clone())).unwrap();
335			blocks.push(block);
336		}
337
338		for block in to_finalize {
339			let hash = blocks[*block as usize - 1].hash();
340			client.finalize_block(hash, None).unwrap();
341		}
342		(client, backend, blocks)
343	}
344
345	fn store_best_justification(client: &TestClient, just: &GrandpaJustification<Block>) {
346		client
347			.lock_import_and_run(|import_op| {
348				crate::aux_schema::update_best_justification(just, |insert| {
349					apply_aux(import_op, insert, &[])
350				})
351			})
352			.unwrap();
353	}
354
355	#[test]
356	fn finality_proof_fails_if_no_more_last_finalized_blocks() {
357		let (_, backend, _) = test_blockchain(6, &[4]);
358		let authority_set_changes = AuthoritySetChanges::empty();
359
360		// The last finalized block is 4, so we cannot provide further justifications.
361		let proof_of_5 = prove_finality(&*backend, authority_set_changes, 5, true);
362		assert!(matches!(proof_of_5, Err(FinalityProofError::BlockNotYetFinalized)));
363	}
364
365	#[test]
366	fn finality_proof_is_none_if_no_justification_known() {
367		let (_, backend, _) = test_blockchain(6, &[4]);
368
369		let mut authority_set_changes = AuthoritySetChanges::empty();
370		authority_set_changes.append(0, 4);
371
372		// Block 4 is finalized without justification
373		// => we can't prove finality of 3
374		let proof_of_3 = prove_finality(&*backend, authority_set_changes, 3, true).unwrap();
375		assert_eq!(proof_of_3, None);
376	}
377
378	#[test]
379	fn finality_proof_check_fails_when_proof_decode_fails() {
380		// When we can't decode proof from Vec<u8>
381		check_finality_proof::<Block>(
382			1,
383			vec![(UncheckedFrom::unchecked_from([3u8; 32]), 1u64)],
384			vec![42],
385		)
386		.unwrap_err();
387	}
388
389	#[test]
390	fn finality_proof_check_fails_when_proof_is_empty() {
391		// When decoded proof has zero length
392		check_finality_proof::<Block>(
393			1,
394			vec![(UncheckedFrom::unchecked_from([3u8; 32]), 1u64)],
395			Vec::<GrandpaJustification<Block>>::new().encode(),
396		)
397		.unwrap_err();
398	}
399
400	#[test]
401	fn finality_proof_check_fails_with_incomplete_justification() {
402		let (_, _, blocks) = test_blockchain(8, &[4, 5, 8]);
403
404		// Create a commit without precommits
405		let commit = finality_grandpa::Commit {
406			target_hash: blocks[7].hash(),
407			target_number: *blocks[7].header().number(),
408			precommits: Vec::new(),
409		};
410
411		let grandpa_just: GrandpaJustification<Block> =
412			sp_consensus_grandpa::GrandpaJustification::<Header> {
413				round: 8,
414				votes_ancestries: Vec::new(),
415				commit,
416			}
417			.into();
418
419		let finality_proof = FinalityProof {
420			block: header(2).hash(),
421			justification: grandpa_just.encode(),
422			unknown_headers: Vec::new(),
423		};
424
425		check_finality_proof::<Block>(
426			1,
427			vec![(UncheckedFrom::unchecked_from([3u8; 32]), 1u64)],
428			finality_proof.encode(),
429		)
430		.unwrap_err();
431	}
432
433	fn create_commit<S, Id>(
434		block: Block,
435		round: u64,
436		set_id: SetId,
437		auth: &[Ed25519Keyring],
438	) -> finality_grandpa::Commit<H256, u64, S, Id>
439	where
440		Id: From<sp_core::ed25519::Public>,
441		S: From<sp_core::ed25519::Signature>,
442	{
443		let mut precommits = Vec::new();
444
445		for voter in auth {
446			let precommit = finality_grandpa::Precommit {
447				target_hash: block.hash(),
448				target_number: *block.header().number(),
449			};
450
451			let msg = finality_grandpa::Message::Precommit(precommit.clone());
452			let encoded = sp_consensus_grandpa::localized_payload(round, set_id, &msg);
453			let signature = voter.sign(&encoded[..]).into();
454
455			let signed_precommit = finality_grandpa::SignedPrecommit {
456				precommit,
457				signature,
458				id: voter.public().into(),
459			};
460			precommits.push(signed_precommit);
461		}
462
463		finality_grandpa::Commit {
464			target_hash: block.hash(),
465			target_number: *block.header().number(),
466			precommits,
467		}
468	}
469
470	#[test]
471	fn finality_proof_check_works_with_correct_justification() {
472		let (client, _, blocks) = test_blockchain(8, &[4, 5, 8]);
473
474		let alice = Ed25519Keyring::Alice;
475		let set_id = 1;
476		let round = 8;
477		let commit = create_commit(blocks[7].clone(), round, set_id, &[alice]);
478		let grandpa_just = GrandpaJustification::from_commit(&client, round, commit).unwrap();
479
480		let finality_proof = FinalityProof {
481			block: header(2).hash(),
482			justification: grandpa_just.encode(),
483			unknown_headers: Vec::new(),
484		};
485		assert_eq!(
486			finality_proof,
487			check_finality_proof::<Block>(
488				set_id,
489				vec![(alice.public().into(), 1u64)],
490				finality_proof.encode(),
491			)
492			.unwrap(),
493		);
494	}
495
496	#[test]
497	fn finality_proof_using_authority_set_changes_fails_with_undefined_start() {
498		let (_, backend, _) = test_blockchain(8, &[4, 5, 8]);
499
500		// We have stored the correct block number for the relevant set, but as we are missing the
501		// block for the preceding set the start is not well-defined.
502		let mut authority_set_changes = AuthoritySetChanges::empty();
503		authority_set_changes.append(1, 8);
504
505		let proof_of_6 = prove_finality(&*backend, authority_set_changes, 6, true);
506		assert!(matches!(proof_of_6, Err(FinalityProofError::BlockNotInAuthoritySetChanges)));
507	}
508
509	#[test]
510	fn finality_proof_using_authority_set_changes_works() {
511		let (client, backend, blocks) = test_blockchain(8, &[4, 5]);
512		let block7 = &blocks[6];
513		let block8 = &blocks[7];
514
515		let round = 8;
516		let commit = create_commit(block8.clone(), round, 1, &[Ed25519Keyring::Alice]);
517		let grandpa_just8 = GrandpaJustification::from_commit(&client, round, commit).unwrap();
518
519		client
520			.finalize_block(block8.hash(), Some((ID, grandpa_just8.encode().clone())))
521			.unwrap();
522
523		// Authority set change at block 8, so the justification stored there will be used in the
524		// FinalityProof for block 6
525		let mut authority_set_changes = AuthoritySetChanges::empty();
526		authority_set_changes.append(0, 5);
527		authority_set_changes.append(1, 8);
528
529		let proof_of_6: FinalityProof =
530			prove_finality(&*backend, authority_set_changes.clone(), 6, true)
531				.unwrap()
532				.unwrap();
533
534		assert_eq!(
535			proof_of_6,
536			FinalityProof {
537				block: block8.hash(),
538				justification: grandpa_just8.encode(),
539				unknown_headers: vec![block7.header().clone(), block8.header().clone()],
540			},
541		);
542
543		let proof_of_6_without_unknown: FinalityProof =
544			prove_finality(&*backend, authority_set_changes.clone(), 6, false)
545				.unwrap()
546				.unwrap();
547
548		assert_eq!(
549			proof_of_6_without_unknown,
550			FinalityProof {
551				block: block8.hash(),
552				justification: grandpa_just8.encode(),
553				unknown_headers: vec![],
554			},
555		);
556	}
557
558	#[test]
559	fn finality_proof_in_last_set_fails_without_latest() {
560		let (_, backend, _) = test_blockchain(8, &[4, 5, 8]);
561
562		// No recent authority set change, so we are in the latest set, and we will try to pickup
563		// the best stored justification, for which there is none in this case.
564		let mut authority_set_changes = AuthoritySetChanges::empty();
565		authority_set_changes.append(0, 5);
566
567		assert!(matches!(prove_finality(&*backend, authority_set_changes, 6, true), Ok(None)));
568	}
569
570	#[test]
571	fn finality_proof_in_last_set_using_latest_justification_works() {
572		let (client, backend, blocks) = test_blockchain(8, &[4, 5, 8]);
573		let block7 = &blocks[6];
574		let block8 = &blocks[7];
575
576		let round = 8;
577		let commit = create_commit(block8.clone(), round, 1, &[Ed25519Keyring::Alice]);
578		let grandpa_just8 = GrandpaJustification::from_commit(&client, round, commit).unwrap();
579		store_best_justification(&client, &grandpa_just8);
580
581		// No recent authority set change, so we are in the latest set, and will pickup the best
582		// stored justification
583		let mut authority_set_changes = AuthoritySetChanges::empty();
584		authority_set_changes.append(0, 5);
585
586		let proof_of_6: FinalityProof =
587			prove_finality(&*backend, authority_set_changes, 6, true).unwrap().unwrap();
588
589		assert_eq!(
590			proof_of_6,
591			FinalityProof {
592				block: block8.hash(),
593				justification: grandpa_just8.encode(),
594				unknown_headers: vec![block7.header().clone(), block8.header().clone()],
595			}
596		);
597	}
598}