1use std::sync::Arc;
20
21use log::debug;
22
23use sp_api::ProvideRuntimeApi;
24use sp_consensus::Error as ConsensusError;
25use sp_consensus_beefy::{AuthorityIdBound, BeefyApi, BEEFY_ENGINE_ID};
26use sp_runtime::{
27 traits::{Block as BlockT, Header as HeaderT, NumberFor},
28 EncodedJustification,
29};
30
31use sc_client_api::{backend::Backend, TrieCacheContext};
32use sc_consensus::{BlockCheckParams, BlockImport, BlockImportParams, ImportResult};
33
34use crate::{
35 communication::notification::BeefyVersionedFinalityProofSender,
36 justification::{decode_and_verify_finality_proof, BeefyVersionedFinalityProof},
37 metric_inc,
38 metrics::BlockImportMetrics,
39 LOG_TARGET,
40};
41
42pub struct BeefyBlockImport<Block: BlockT, Backend, RuntimeApi, I, AuthorityId: AuthorityIdBound> {
49 backend: Arc<Backend>,
50 runtime: Arc<RuntimeApi>,
51 inner: I,
52 justification_sender: BeefyVersionedFinalityProofSender<Block, AuthorityId>,
53 metrics: Option<BlockImportMetrics>,
54}
55
56impl<Block: BlockT, BE, Runtime, I: Clone, AuthorityId: AuthorityIdBound> Clone
57 for BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
58{
59 fn clone(&self) -> Self {
60 BeefyBlockImport {
61 backend: self.backend.clone(),
62 runtime: self.runtime.clone(),
63 inner: self.inner.clone(),
64 justification_sender: self.justification_sender.clone(),
65 metrics: self.metrics.clone(),
66 }
67 }
68}
69
70impl<Block: BlockT, BE, Runtime, I, AuthorityId: AuthorityIdBound>
71 BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
72{
73 pub fn new(
75 backend: Arc<BE>,
76 runtime: Arc<Runtime>,
77 inner: I,
78 justification_sender: BeefyVersionedFinalityProofSender<Block, AuthorityId>,
79 metrics: Option<BlockImportMetrics>,
80 ) -> BeefyBlockImport<Block, BE, Runtime, I, AuthorityId> {
81 BeefyBlockImport { backend, runtime, inner, justification_sender, metrics }
82 }
83}
84
85impl<Block, BE, Runtime, I, AuthorityId> BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
86where
87 Block: BlockT,
88 BE: Backend<Block>,
89 Runtime: ProvideRuntimeApi<Block>,
90 Runtime::Api: BeefyApi<Block, AuthorityId> + Send,
91 AuthorityId: AuthorityIdBound,
92{
93 fn decode_and_verify(
94 &self,
95 encoded: &EncodedJustification,
96 number: NumberFor<Block>,
97 hash: <Block as BlockT>::Hash,
98 ) -> Result<BeefyVersionedFinalityProof<Block, AuthorityId>, ConsensusError> {
99 use ConsensusError::ClientImport as ImportError;
100 let beefy_genesis = self
101 .runtime
102 .runtime_api()
103 .beefy_genesis(hash)
104 .map_err(|e| ImportError(e.to_string()))?
105 .ok_or_else(|| ImportError("Unknown BEEFY genesis".to_string()))?;
106 if number < beefy_genesis {
107 return Err(ImportError("BEEFY genesis is set for future block".to_string()));
108 }
109 let validator_set = self
110 .runtime
111 .runtime_api()
112 .validator_set(hash)
113 .map_err(|e| ImportError(e.to_string()))?
114 .ok_or_else(|| ImportError("Unknown validator set".to_string()))?;
115
116 decode_and_verify_finality_proof::<Block, AuthorityId>(&encoded[..], number, &validator_set)
117 .map_err(|(err, _)| err)
118 }
119}
120
121#[async_trait::async_trait]
122impl<Block, BE, Runtime, I, AuthorityId> BlockImport<Block>
123 for BeefyBlockImport<Block, BE, Runtime, I, AuthorityId>
124where
125 Block: BlockT,
126 BE: Backend<Block>,
127 I: BlockImport<Block, Error = ConsensusError> + Send + Sync,
128 Runtime: ProvideRuntimeApi<Block> + Send + Sync,
129 Runtime::Api: BeefyApi<Block, AuthorityId>,
130 AuthorityId: AuthorityIdBound,
131{
132 type Error = ConsensusError;
133
134 async fn import_block(
135 &self,
136 mut block: BlockImportParams<Block>,
137 ) -> Result<ImportResult, Self::Error> {
138 let hash = block.post_hash();
139 let number = *block.header.number();
140
141 let beefy_encoded = block.justifications.as_mut().and_then(|just| {
142 let encoded = just.get(BEEFY_ENGINE_ID).cloned();
143 just.remove(BEEFY_ENGINE_ID);
146 encoded
147 });
148
149 let inner_import_result = self.inner.import_block(block).await?;
151
152 match self.backend.state_at(hash, TrieCacheContext::Untrusted) {
153 Ok(_) => {},
154 Err(_) => {
155 return Ok(inner_import_result);
159 },
160 }
161
162 match (beefy_encoded, &inner_import_result) {
163 (Some(encoded), ImportResult::Imported(_)) => {
164 match self.decode_and_verify(&encoded, number, hash) {
165 Ok(proof) => {
166 debug!(
168 target: LOG_TARGET,
169 "🥩 import justif {} for block number {:?}.", proof, number
170 );
171 self.justification_sender
173 .notify(|| Ok::<_, ()>(proof))
174 .expect("the closure always returns Ok; qed.");
175 metric_inc!(self.metrics, beefy_good_justification_imports);
176 },
177 Err(err) => {
178 debug!(
179 target: LOG_TARGET,
180 "🥩 error importing BEEFY justification for block {:?}: {:?}",
181 number,
182 err,
183 );
184 metric_inc!(self.metrics, beefy_bad_justification_imports);
185 },
186 }
187 },
188 _ => (),
189 }
190
191 Ok(inner_import_result)
192 }
193
194 async fn check_block(
195 &self,
196 block: BlockCheckParams<Block>,
197 ) -> Result<ImportResult, Self::Error> {
198 self.inner.check_block(block).await
199 }
200}