cuprated/blockchain/
chain_service.rs

1use std::task::{Context, Poll};
2
3use futures::{future::BoxFuture, FutureExt, TryFutureExt};
4use tower::Service;
5
6use cuprate_blockchain::service::BlockchainReadHandle;
7use cuprate_fast_sync::validate_entries;
8use cuprate_p2p::block_downloader::{ChainSvcRequest, ChainSvcResponse};
9use cuprate_p2p_core::NetworkZone;
10use cuprate_types::blockchain::{BlockchainReadRequest, BlockchainResponse};
11
12/// That service that allows retrieving the chain state to give to the P2P crates, so we can figure out
13/// what blocks we need.
14///
15/// This has a more minimal interface than [`BlockchainReadRequest`] to make using the p2p crates easier.
16#[derive(Clone)]
17pub struct ChainService(pub BlockchainReadHandle);
18
19impl<N: NetworkZone> Service<ChainSvcRequest<N>> for ChainService {
20    type Response = ChainSvcResponse<N>;
21    type Error = tower::BoxError;
22    type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
23
24    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
25        self.0.poll_ready(cx).map_err(Into::into)
26    }
27
28    fn call(&mut self, req: ChainSvcRequest<N>) -> Self::Future {
29        let map_res = |res: BlockchainResponse| match res {
30            BlockchainResponse::CompactChainHistory {
31                block_ids,
32                cumulative_difficulty,
33            } => ChainSvcResponse::CompactHistory {
34                block_ids,
35                cumulative_difficulty,
36            },
37            BlockchainResponse::FindFirstUnknown(res) => ChainSvcResponse::FindFirstUnknown(res),
38            _ => unreachable!(),
39        };
40
41        match req {
42            ChainSvcRequest::CompactHistory => self
43                .0
44                .call(BlockchainReadRequest::CompactChainHistory)
45                .map_ok(map_res)
46                .map_err(Into::into)
47                .boxed(),
48            ChainSvcRequest::FindFirstUnknown(req) => self
49                .0
50                .call(BlockchainReadRequest::FindFirstUnknown(req))
51                .map_ok(map_res)
52                .map_err(Into::into)
53                .boxed(),
54            ChainSvcRequest::CumulativeDifficulty => self
55                .0
56                .call(BlockchainReadRequest::CompactChainHistory)
57                .map_ok(|res| {
58                    // TODO create a custom request instead of hijacking this one.
59                    // TODO: use the context cache.
60                    let BlockchainResponse::CompactChainHistory {
61                        cumulative_difficulty,
62                        ..
63                    } = res
64                    else {
65                        unreachable!()
66                    };
67
68                    ChainSvcResponse::CumulativeDifficulty(cumulative_difficulty)
69                })
70                .map_err(Into::into)
71                .boxed(),
72            ChainSvcRequest::ValidateEntries(entries, start_height) => {
73                let mut blockchain_read_handle = self.0.clone();
74
75                async move {
76                    let (valid, unknown) =
77                        validate_entries(entries, start_height, &mut blockchain_read_handle)
78                            .await?;
79
80                    Ok(ChainSvcResponse::ValidateEntries { valid, unknown })
81                }
82                .boxed()
83            }
84        }
85    }
86}