1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Copyright (C) Parity Technologies (UK) Ltd.
// This file is part of Parity Bridges Common.

// Parity Bridges Common is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Parity Bridges Common is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Parity Bridges Common.  If not, see <http://www.gnu.org/licenses/>.

//! Logic for optimizing GRANDPA Finality Proofs.

use crate::justification::{
	verification::{Error, JustificationVerifier, PrecommitError},
	GrandpaJustification,
};

use crate::justification::verification::{
	IterationFlow, JustificationVerificationContext, SignedPrecommit,
};
use sp_consensus_grandpa::AuthorityId;
use sp_runtime::traits::Header as HeaderT;
use sp_std::{collections::btree_set::BTreeSet, prelude::*};

// Verification callbacks for justification optimization.
struct JustificationOptimizer<Header: HeaderT> {
	votes: BTreeSet<AuthorityId>,

	extra_precommits: Vec<usize>,
	redundant_votes_ancestries: BTreeSet<Header::Hash>,
}

impl<Header: HeaderT> JustificationOptimizer<Header> {
	fn optimize(self, justification: &mut GrandpaJustification<Header>) {
		for invalid_precommit_idx in self.extra_precommits.into_iter().rev() {
			justification.commit.precommits.remove(invalid_precommit_idx);
		}
		if !self.redundant_votes_ancestries.is_empty() {
			justification
				.votes_ancestries
				.retain(|header| !self.redundant_votes_ancestries.contains(&header.hash()))
		}
	}
}

impl<Header: HeaderT> JustificationVerifier<Header> for JustificationOptimizer<Header> {
	fn process_redundant_vote(
		&mut self,
		precommit_idx: usize,
	) -> Result<IterationFlow, PrecommitError> {
		self.extra_precommits.push(precommit_idx);
		Ok(IterationFlow::Skip)
	}

	fn process_known_authority_vote(
		&mut self,
		precommit_idx: usize,
		signed: &SignedPrecommit<Header>,
	) -> Result<IterationFlow, PrecommitError> {
		// Skip duplicate votes
		if self.votes.contains(&signed.id) {
			self.extra_precommits.push(precommit_idx);
			return Ok(IterationFlow::Skip)
		}

		Ok(IterationFlow::Run)
	}

	fn process_unknown_authority_vote(
		&mut self,
		precommit_idx: usize,
	) -> Result<(), PrecommitError> {
		self.extra_precommits.push(precommit_idx);
		Ok(())
	}

	fn process_unrelated_ancestry_vote(
		&mut self,
		precommit_idx: usize,
	) -> Result<IterationFlow, PrecommitError> {
		self.extra_precommits.push(precommit_idx);
		Ok(IterationFlow::Skip)
	}

	fn process_invalid_signature_vote(
		&mut self,
		precommit_idx: usize,
	) -> Result<(), PrecommitError> {
		self.extra_precommits.push(precommit_idx);
		Ok(())
	}

	fn process_valid_vote(&mut self, signed: &SignedPrecommit<Header>) {
		self.votes.insert(signed.id.clone());
	}

	fn process_redundant_votes_ancestries(
		&mut self,
		redundant_votes_ancestries: BTreeSet<Header::Hash>,
	) -> Result<(), Error> {
		self.redundant_votes_ancestries = redundant_votes_ancestries;
		Ok(())
	}
}

/// Verify and optimize given justification by removing unknown and duplicate votes.
pub fn verify_and_optimize_justification<Header: HeaderT>(
	finalized_target: (Header::Hash, Header::Number),
	context: &JustificationVerificationContext,
	justification: &mut GrandpaJustification<Header>,
) -> Result<(), Error> {
	let mut optimizer = JustificationOptimizer {
		votes: BTreeSet::new(),
		extra_precommits: vec![],
		redundant_votes_ancestries: Default::default(),
	};
	optimizer.verify_justification(finalized_target, context, justification)?;
	optimizer.optimize(justification);

	Ok(())
}