1use 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#[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 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 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 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 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#[derive(Debug, PartialEq, Encode, Decode, Clone)]
139pub struct FinalityProof<Header: HeaderT> {
140 pub block: Header::Hash,
142 pub justification: Vec<u8>,
144 pub unknown_headers: Vec<Header>,
146}
147
148#[derive(Debug, thiserror::Error)]
150pub enum FinalityProofError {
151 #[error("Block not yet finalized")]
153 BlockNotYetFinalized,
154 #[error("Block not covered by authority set changes")]
157 BlockNotInAuthoritySetChanges,
158 #[error(transparent)]
160 Client(#[from] sp_blockchain::Error),
161}
162
163fn 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 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 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 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, ¤t_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 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 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 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 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 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 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 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 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 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}