sc_consensus_beefy/
fisherman.rs1use crate::{error::Error, keystore::BeefyKeystore, round::Rounds, LOG_TARGET};
20use log::{debug, error, warn};
21use sc_client_api::Backend;
22use sp_api::ProvideRuntimeApi;
23use sp_application_crypto::RuntimeAppPublic;
24use sp_blockchain::HeaderBackend;
25use sp_consensus_beefy::{
26 check_double_voting_proof, AuthorityIdBound, BeefyApi, BeefySignatureHasher, DoubleVotingProof,
27 OpaqueKeyOwnershipProof, ValidatorSetId,
28};
29use sp_runtime::{
30 generic::BlockId,
31 traits::{Block, NumberFor},
32};
33use std::{marker::PhantomData, sync::Arc};
34
35pub struct ProvedValidator {
37 pub key_owner_proof: OpaqueKeyOwnershipProof,
38}
39
40pub struct Fisherman<B, BE, RuntimeApi, AuthorityId: AuthorityIdBound> {
42 backend: Arc<BE>,
43 runtime: Arc<RuntimeApi>,
44 key_store: Arc<BeefyKeystore<AuthorityId>>,
45
46 _phantom: PhantomData<B>,
47}
48
49impl<B: Block, BE: Backend<B>, RuntimeApi: ProvideRuntimeApi<B>, AuthorityId>
50 Fisherman<B, BE, RuntimeApi, AuthorityId>
51where
52 RuntimeApi::Api: BeefyApi<B, AuthorityId>,
53 AuthorityId: AuthorityIdBound,
54{
55 pub fn new(
56 backend: Arc<BE>,
57 runtime: Arc<RuntimeApi>,
58 keystore: Arc<BeefyKeystore<AuthorityId>>,
59 ) -> Self {
60 Self { backend, runtime, key_store: keystore, _phantom: Default::default() }
61 }
62
63 fn prove_offenders<'a>(
64 &self,
65 at: BlockId<B>,
66 offender_ids: impl Iterator<Item = &'a AuthorityId>,
67 validator_set_id: ValidatorSetId,
68 ) -> Result<Vec<ProvedValidator>, Error> {
69 let hash = match at {
70 BlockId::Hash(hash) => hash,
71 BlockId::Number(number) => self
72 .backend
73 .blockchain()
74 .expect_block_hash_from_id(&BlockId::Number(number))
75 .map_err(|err| {
76 Error::Backend(format!(
77 "Couldn't get hash for block #{:?} (error: {:?}). \
78 Skipping report for equivocation",
79 at, err
80 ))
81 })?,
82 };
83
84 let runtime_api = self.runtime.runtime_api();
85 let mut proved_offenders = vec![];
86 for offender_id in offender_ids {
87 match runtime_api.generate_key_ownership_proof(
88 hash,
89 validator_set_id,
90 offender_id.clone(),
91 ) {
92 Ok(Some(key_owner_proof)) => {
93 proved_offenders.push(ProvedValidator { key_owner_proof });
94 },
95 Ok(None) => {
96 debug!(
97 target: LOG_TARGET,
98 "🥩 Equivocation offender {} not part of the authority set {}.",
99 offender_id, validator_set_id
100 );
101 },
102 Err(e) => {
103 error!(
104 target: LOG_TARGET,
105 "🥩 Error generating key ownership proof for equivocation offender {} \
106 in authority set {}: {}",
107 offender_id, validator_set_id, e
108 );
109 },
110 };
111 }
112
113 Ok(proved_offenders)
114 }
115
116 pub fn report_double_voting(
122 &self,
123 proof: DoubleVotingProof<
124 NumberFor<B>,
125 AuthorityId,
126 <AuthorityId as RuntimeAppPublic>::Signature,
127 >,
128 active_rounds: &Rounds<B, AuthorityId>,
129 ) -> Result<(), Error> {
130 let (validators, validator_set_id) =
131 (active_rounds.validators(), active_rounds.validator_set_id());
132 let offender_id = proof.offender_id();
133
134 if !check_double_voting_proof::<_, _, BeefySignatureHasher>(&proof) {
135 debug!(target: LOG_TARGET, "🥩 Skipping report for bad equivocation {:?}", proof);
136 return Ok(());
137 }
138
139 if let Some(local_id) = self.key_store.authority_id(validators) {
140 if offender_id == &local_id {
141 warn!(target: LOG_TARGET, "🥩 Skipping report for own equivocation");
142 return Ok(());
143 }
144 }
145
146 let key_owner_proofs = self.prove_offenders(
147 BlockId::Number(*proof.round_number()),
148 vec![offender_id].into_iter(),
149 validator_set_id,
150 )?;
151
152 let best_block_hash = self.backend.blockchain().info().best_hash;
154 for ProvedValidator { key_owner_proof, .. } in key_owner_proofs {
155 self.runtime
156 .runtime_api()
157 .submit_report_double_voting_unsigned_extrinsic(
158 best_block_hash,
159 proof.clone(),
160 key_owner_proof,
161 )
162 .map_err(Error::RuntimeApi)?;
163 }
164
165 Ok(())
166 }
167}