1use crate::error::Error;
6use crate::types::{GetBlock, GetBlockHeader, GetBlockchainInfo};
7use bitcoin::consensus::Encodable;
8use bitcoin::{Block as BitcoinBlock, BlockHash};
9use jsonrpsee::proc_macros::rpc;
10use sc_client_api::{AuxStore, BlockBackend, HeaderBackend};
11use sp_runtime::traits::{Block as BlockT, SaturatedConversion};
12use std::marker::PhantomData;
13use std::sync::Arc;
14use subcoin_primitives::{BackendExt, BitcoinTransactionAdapter, convert_to_bitcoin_block};
15
16#[rpc(client, server)]
18pub trait BlockchainApi {
19 #[method(name = "getbestblockhash", blocking)]
21 fn get_best_block_hash(&self) -> Result<BlockHash, Error>;
22
23 #[method(name = "getblockchaininfo", blocking)]
25 fn get_blockchain_info(&self) -> Result<GetBlockchainInfo, Error>;
26
27 #[method(name = "getblockcount", blocking)]
29 fn get_block_count(&self) -> Result<u32, Error>;
30
31 #[method(name = "getblockhash", blocking)]
33 fn get_block_hash(&self, height: u32) -> Result<BlockHash, Error>;
34
35 #[method(name = "getblock", blocking)]
41 fn get_block(
42 &self,
43 blockhash: BlockHash,
44 verbosity: Option<u8>,
45 ) -> Result<serde_json::Value, Error>;
46
47 #[method(name = "getblockheader", blocking)]
52 fn get_block_header(
53 &self,
54 blockhash: BlockHash,
55 verbose: Option<bool>,
56 ) -> Result<serde_json::Value, Error>;
57}
58
59fn difficulty_from_bits(bits: u32) -> f64 {
61 let mantissa = bits & 0x00ff_ffff;
64 let exponent = (bits >> 24) as i32;
65
66 if mantissa == 0 {
67 return 0.0;
68 }
69
70 let shift = 8 * (exponent - 3);
73 let diff = (0x0000ffff_u64 as f64) / (mantissa as f64) * 2f64.powi(-shift);
74 if diff > 0.0 { diff } else { 0.0 }
75}
76
77pub struct Blockchain<Block, Client, TransactionAdapter> {
79 client: Arc<Client>,
80 network: bitcoin::Network,
81 _phantom: PhantomData<(Block, TransactionAdapter)>,
82}
83
84impl<Block, Client, TransactionAdapter> Blockchain<Block, Client, TransactionAdapter>
85where
86 Block: BlockT + 'static,
87 Client: HeaderBackend<Block> + BlockBackend<Block> + AuxStore + 'static,
88{
89 pub fn new(client: Arc<Client>, network: bitcoin::Network) -> Self {
91 Self {
92 client,
93 network,
94 _phantom: Default::default(),
95 }
96 }
97
98 fn get_bitcoin_block(&self, block_hash: BlockHash) -> Result<BitcoinBlock, Error>
99 where
100 TransactionAdapter: BitcoinTransactionAdapter<Block>,
101 {
102 let substrate_block_hash = self
103 .client
104 .substrate_block_hash_for(block_hash)
105 .ok_or(Error::BlockNotFound)?;
106
107 let substrate_block = self
108 .client
109 .block(substrate_block_hash)?
110 .ok_or(Error::BlockNotFound)?
111 .block;
112
113 convert_to_bitcoin_block::<Block, TransactionAdapter>(substrate_block)
114 .map_err(Error::Header)
115 }
116
117 fn get_block_number(&self, block_hash: BlockHash) -> Result<u32, Error> {
118 self.client
119 .block_number(block_hash)
120 .ok_or(Error::BlockNotFound)
121 }
122
123 fn best_number(&self) -> u32 {
124 self.client.info().best_number.saturated_into()
125 }
126
127 fn calculate_confirmations(&self, block_height: u32) -> i32 {
128 let best = self.best_number();
129 if block_height > best {
130 0
131 } else {
132 (best - block_height + 1) as i32
133 }
134 }
135
136 fn get_next_block_hash(&self, block_height: u32) -> Option<BlockHash> {
137 self.client.block_hash(block_height + 1)
138 }
139
140 fn block_to_get_block(&self, block: &BitcoinBlock, height: u32) -> GetBlock {
141 let header = &block.header;
142 let block_hash = header.block_hash();
143 let txids = block.txdata.iter().map(|tx| tx.compute_txid()).collect();
144
145 let mut size_data = Vec::new();
147 block
148 .consensus_encode(&mut size_data)
149 .expect("encoding should not fail");
150 let size = size_data.len() as u32;
151
152 let weight = block.weight().to_wu() as u32;
154 let strippedsize = (4 * size).saturating_sub(weight) / 3;
158
159 GetBlock {
160 hash: block_hash,
161 confirmations: self.calculate_confirmations(height),
162 size,
163 strippedsize,
164 weight,
165 height,
166 version: header.version.to_consensus(),
167 version_hex: format!("{:08x}", header.version.to_consensus()),
168 merkleroot: header.merkle_root.to_string(),
169 tx: txids,
170 time: header.time,
171 mediantime: header.time, nonce: header.nonce,
173 bits: format!("{:08x}", header.bits.to_consensus()),
174 difficulty: difficulty_from_bits(header.bits.to_consensus()),
175 chainwork: "0".to_string(), n_tx: block.txdata.len() as u32,
177 previousblockhash: if height > 0 {
178 Some(header.prev_blockhash)
179 } else {
180 None
181 },
182 nextblockhash: self.get_next_block_hash(height),
183 }
184 }
185
186 fn header_to_get_block_header(
187 &self,
188 header: &bitcoin::block::Header,
189 height: u32,
190 n_tx: u32,
191 ) -> GetBlockHeader {
192 let block_hash = header.block_hash();
193
194 GetBlockHeader {
195 hash: block_hash,
196 confirmations: self.calculate_confirmations(height),
197 height,
198 version: header.version.to_consensus(),
199 version_hex: format!("{:08x}", header.version.to_consensus()),
200 merkleroot: header.merkle_root.to_string(),
201 time: header.time,
202 mediantime: header.time, nonce: header.nonce,
204 bits: format!("{:08x}", header.bits.to_consensus()),
205 difficulty: difficulty_from_bits(header.bits.to_consensus()),
206 chainwork: "0".to_string(), n_tx,
208 previousblockhash: if height > 0 {
209 Some(header.prev_blockhash)
210 } else {
211 None
212 },
213 nextblockhash: self.get_next_block_hash(height),
214 }
215 }
216}
217
218#[async_trait::async_trait]
219impl<Block, Client, TransactionAdapter> BlockchainApiServer
220 for Blockchain<Block, Client, TransactionAdapter>
221where
222 Block: BlockT + 'static,
223 Client: HeaderBackend<Block> + BlockBackend<Block> + AuxStore + 'static,
224 TransactionAdapter: BitcoinTransactionAdapter<Block> + Send + Sync + 'static,
225{
226 fn get_best_block_hash(&self) -> Result<BlockHash, Error> {
227 let best_substrate_hash = self.client.info().best_hash;
228 self.client
229 .bitcoin_block_hash_for(best_substrate_hash)
230 .ok_or_else(|| Error::Other("Best block hash not found".to_string()))
231 }
232
233 fn get_blockchain_info(&self) -> Result<GetBlockchainInfo, Error> {
234 let best_hash = self.get_best_block_hash()?;
235 let best_number = self.best_number();
236
237 let block = self.get_bitcoin_block(best_hash)?;
239 let difficulty = difficulty_from_bits(block.header.bits.to_consensus());
240
241 Ok(GetBlockchainInfo {
242 chain: self.network.to_core_arg().to_string(),
243 blocks: best_number,
244 headers: best_number, bestblockhash: best_hash,
246 difficulty,
247 verificationprogress: 1.0, initialblockdownload: false,
249 chainwork: "0".to_string(), size_on_disk: 0, pruned: false,
252 warnings: vec![],
253 })
254 }
255
256 fn get_block_count(&self) -> Result<u32, Error> {
257 Ok(self.best_number())
258 }
259
260 fn get_block_hash(&self, height: u32) -> Result<BlockHash, Error> {
261 self.client
262 .block_hash(height)
263 .ok_or(Error::BlockHeightOutOfRange)
264 }
265
266 fn get_block(
267 &self,
268 blockhash: BlockHash,
269 verbosity: Option<u8>,
270 ) -> Result<serde_json::Value, Error> {
271 let block = self.get_bitcoin_block(blockhash)?;
272 let height = self.get_block_number(blockhash)?;
273
274 match verbosity.unwrap_or(1) {
275 0 => {
276 let mut data = Vec::new();
278 block
279 .consensus_encode(&mut data)
280 .map_err(|e| Error::Other(format!("Failed to encode block: {e}")))?;
281 Ok(serde_json::Value::String(hex::encode(data)))
282 }
283 1 => {
284 let get_block = self.block_to_get_block(&block, height);
286 Ok(serde_json::to_value(get_block)?)
287 }
288 2 => {
289 let get_block = self.block_to_get_block(&block, height);
292 Ok(serde_json::to_value(get_block)?)
293 }
294 _ => Err(Error::Other("Invalid verbosity level".to_string())),
295 }
296 }
297
298 fn get_block_header(
299 &self,
300 blockhash: BlockHash,
301 verbose: Option<bool>,
302 ) -> Result<serde_json::Value, Error> {
303 let block = self.get_bitcoin_block(blockhash)?;
304 let height = self.get_block_number(blockhash)?;
305
306 if verbose.unwrap_or(true) {
307 let header_info =
309 self.header_to_get_block_header(&block.header, height, block.txdata.len() as u32);
310 Ok(serde_json::to_value(header_info)?)
311 } else {
312 let mut data = Vec::new();
314 block
315 .header
316 .consensus_encode(&mut data)
317 .map_err(|e| Error::Other(format!("Failed to encode header: {e}")))?;
318 Ok(serde_json::Value::String(hex::encode(data)))
319 }
320 }
321}