sc_consensus/
longest_chain.rs1use sc_client_api::backend;
22use sp_blockchain::{Backend, HeaderBackend};
23use sp_consensus::{Error as ConsensusError, SelectChain};
24use sp_runtime::traits::{Block as BlockT, Header, NumberFor};
25use std::{marker::PhantomData, sync::Arc};
26
27pub struct LongestChain<B, Block> {
30 backend: Arc<B>,
31 _phantom: PhantomData<Block>,
32}
33
34impl<B, Block> Clone for LongestChain<B, Block> {
35 fn clone(&self) -> Self {
36 let backend = self.backend.clone();
37 LongestChain { backend, _phantom: Default::default() }
38 }
39}
40
41impl<B, Block> LongestChain<B, Block>
42where
43 B: backend::Backend<Block>,
44 Block: BlockT,
45{
46 pub fn new(backend: Arc<B>) -> Self {
48 LongestChain { backend, _phantom: Default::default() }
49 }
50
51 fn best_hash(&self) -> sp_blockchain::Result<<Block as BlockT>::Hash> {
52 let info = self.backend.blockchain().info();
53 let import_lock = self.backend.get_import_lock();
54 let best_hash = self
55 .backend
56 .blockchain()
57 .longest_containing(info.best_hash, import_lock)?
58 .unwrap_or(info.best_hash);
59 Ok(best_hash)
60 }
61
62 fn best_header(&self) -> sp_blockchain::Result<<Block as BlockT>::Header> {
63 let best_hash = self.best_hash()?;
64 Ok(self
65 .backend
66 .blockchain()
67 .header(best_hash)?
68 .expect("given block hash was fetched from block in db; qed"))
69 }
70
71 fn finality_target(
81 &self,
82 base_hash: Block::Hash,
83 maybe_max_number: Option<NumberFor<Block>>,
84 ) -> sp_blockchain::Result<Block::Hash> {
85 use sp_blockchain::Error::{Application, MissingHeader};
86 let blockchain = self.backend.blockchain();
87
88 let mut current_head = self.best_header()?;
89 let mut best_hash = current_head.hash();
90
91 let base_header = blockchain
92 .header(base_hash)?
93 .ok_or_else(|| MissingHeader(base_hash.to_string()))?;
94 let base_number = *base_header.number();
95
96 if let Some(max_number) = maybe_max_number {
97 if max_number < base_number {
98 let msg = format!(
99 "Requested a finality target using max number {} below the base number {}",
100 max_number, base_number
101 );
102 return Err(Application(msg.into()))
103 }
104
105 while current_head.number() > &max_number {
106 best_hash = *current_head.parent_hash();
107 current_head = blockchain
108 .header(best_hash)?
109 .ok_or_else(|| MissingHeader(format!("{best_hash:?}")))?;
110 }
111 }
112
113 while current_head.hash() != base_hash {
114 if *current_head.number() < base_number {
115 let msg = format!(
116 "Requested a finality target using a base {:?} not in the best chain {:?}",
117 base_hash, best_hash,
118 );
119 return Err(Application(msg.into()))
120 }
121 let current_hash = *current_head.parent_hash();
122 current_head = blockchain
123 .header(current_hash)?
124 .ok_or_else(|| MissingHeader(format!("{best_hash:?}")))?;
125 }
126
127 Ok(best_hash)
128 }
129
130 fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, sp_blockchain::Error> {
131 self.backend.blockchain().leaves()
132 }
133}
134
135#[async_trait::async_trait]
136impl<B, Block> SelectChain<Block> for LongestChain<B, Block>
137where
138 B: backend::Backend<Block>,
139 Block: BlockT,
140{
141 async fn leaves(&self) -> Result<Vec<<Block as BlockT>::Hash>, ConsensusError> {
142 LongestChain::leaves(self).map_err(|e| ConsensusError::ChainLookup(e.to_string()))
143 }
144
145 async fn best_chain(&self) -> Result<<Block as BlockT>::Header, ConsensusError> {
146 LongestChain::best_header(self).map_err(|e| ConsensusError::ChainLookup(e.to_string()))
147 }
148
149 async fn finality_target(
150 &self,
151 base_hash: Block::Hash,
152 maybe_max_number: Option<NumberFor<Block>>,
153 ) -> Result<Block::Hash, ConsensusError> {
154 LongestChain::finality_target(self, base_hash, maybe_max_number)
155 .map_err(|e| ConsensusError::ChainLookup(e.to_string()))
156 }
157}