cuprated/rpc/
rpc_handler.rs

1//! `cuprated`'s implementation of [`RpcHandler`].
2
3use std::task::{Context, Poll};
4
5use anyhow::Error;
6use futures::future::BoxFuture;
7use monero_serai::block::Block;
8use tower::Service;
9
10use cuprate_blockchain::service::{BlockchainReadHandle, BlockchainWriteHandle};
11use cuprate_consensus::BlockchainContextService;
12use cuprate_pruning::PruningSeed;
13use cuprate_rpc_interface::RpcHandler;
14use cuprate_rpc_types::{
15    bin::{BinRequest, BinResponse},
16    json::{JsonRpcRequest, JsonRpcResponse},
17    other::{OtherRequest, OtherResponse},
18};
19use cuprate_txpool::service::TxpoolReadHandle;
20use cuprate_types::BlockTemplate;
21
22use crate::rpc::handlers;
23
24/// TODO: use real type when public.
25#[derive(Clone)]
26pub enum BlockchainManagerRequest {
27    /// Pop blocks off the top of the blockchain.
28    ///
29    /// Input is the amount of blocks to pop.
30    PopBlocks { amount: usize },
31
32    /// Start pruning the blockchain.
33    Prune,
34
35    /// Is the blockchain pruned?
36    Pruned,
37
38    /// Relay a block to the network.
39    RelayBlock(
40        /// This is [`Box`]ed due to `clippy::large_enum_variant`.
41        Box<Block>,
42    ),
43
44    /// Sync/flush the blockchain database to disk.
45    Sync,
46
47    /// Is the blockchain in the middle of syncing?
48    ///
49    /// This returning `false` does not necessarily
50    /// mean [`BlockchainManagerRequest::Synced`] will
51    /// return `true`, for example, if the network has been
52    /// cut off and we have no peers, this will return `false`,
53    /// however, [`BlockchainManagerRequest::Synced`] may return
54    /// `true` if the latest known chain tip is equal to our height.
55    Syncing,
56
57    /// Is the blockchain fully synced?
58    Synced,
59
60    /// Current target block time.
61    Target,
62
63    /// The height of the next block in the chain.
64    TargetHeight,
65
66    /// Generate new blocks.
67    ///
68    /// This request is only for regtest, see RPC's `generateblocks`.
69    GenerateBlocks {
70        /// Number of the blocks to be generated.
71        amount_of_blocks: u64,
72        /// The previous block's hash.
73        prev_block: Option<[u8; 32]>,
74        /// The starting value for the nonce.
75        starting_nonce: u32,
76        /// The address that will receive the coinbase reward.
77        wallet_address: String,
78    },
79
80    //    // TODO: the below requests actually belong to the block downloader/syncer:
81    //    // <https://github.com/Cuprate/cuprate/pull/320#discussion_r1811089758>
82    //    /// Get [`Span`] data.
83    //    ///
84    //    /// This is data that describes an active downloading process,
85    //    /// if we are fully synced, this will return an empty [`Vec`].
86    //    Spans,
87
88    //
89    /// Get the next [`PruningSeed`] needed for a pruned sync.
90    NextNeededPruningSeed,
91
92    /// Create a block template.
93    CreateBlockTemplate {
94        prev_block: [u8; 32],
95        account_public_address: String,
96        extra_nonce: Vec<u8>,
97    },
98
99    /// Safely shutdown `cuprated`.
100    Stop,
101}
102
103/// TODO: use real type when public.
104#[derive(Clone)]
105pub enum BlockchainManagerResponse {
106    /// General OK response.
107    ///
108    /// Response to:
109    /// - [`BlockchainManagerRequest::Prune`]
110    /// - [`BlockchainManagerRequest::RelayBlock`]
111    /// - [`BlockchainManagerRequest::Sync`]
112    Ok,
113
114    /// Response to [`BlockchainManagerRequest::PopBlocks`]
115    PopBlocks { new_height: usize },
116
117    /// Response to [`BlockchainManagerRequest::Prune`]
118    Prune(PruningSeed),
119
120    /// Response to [`BlockchainManagerRequest::Pruned`]
121    Pruned(bool),
122
123    /// Response to [`BlockchainManagerRequest::Syncing`]
124    Syncing(bool),
125
126    /// Response to [`BlockchainManagerRequest::Synced`]
127    Synced(bool),
128
129    /// Response to [`BlockchainManagerRequest::Target`]
130    Target(std::time::Duration),
131
132    /// Response to [`BlockchainManagerRequest::TargetHeight`]
133    TargetHeight { height: usize },
134
135    /// Response to [`BlockchainManagerRequest::GenerateBlocks`]
136    GenerateBlocks {
137        /// Hashes of the blocks generated.
138        blocks: Vec<[u8; 32]>,
139        /// The new top height. (TODO: is this correct?)
140        height: usize,
141    },
142
143    /// Response to [`BlockchainManagerRequest::NextNeededPruningSeed`].
144    NextNeededPruningSeed(PruningSeed),
145
146    /// Response to [`BlockchainManagerRequest::CreateBlockTemplate`].
147    CreateBlockTemplate(Box<BlockTemplate>),
148}
149
150/// TODO: use real type when public.
151pub type BlockchainManagerHandle = cuprate_database_service::DatabaseReadService<
152    BlockchainManagerRequest,
153    BlockchainManagerResponse,
154>;
155
156/// TODO
157#[derive(Clone)]
158pub struct CupratedRpcHandler {
159    /// Should this RPC server be [restricted](RpcHandler::is_restricted)?
160    ///
161    /// This is not `pub` on purpose, as it should not be mutated after [`Self::new`].
162    restricted: bool,
163
164    /// Read handle to the blockchain database.
165    pub blockchain_read: BlockchainReadHandle,
166
167    /// Handle to the blockchain context service.
168    pub blockchain_context: BlockchainContextService,
169
170    /// Handle to the blockchain manager.
171    pub blockchain_manager: BlockchainManagerHandle,
172
173    /// Read handle to the transaction pool database.
174    pub txpool_read: TxpoolReadHandle,
175
176    /// TODO: handle to txpool service.
177    pub txpool_manager: std::convert::Infallible,
178}
179
180impl CupratedRpcHandler {
181    /// Create a new [`Self`].
182    pub const fn new(
183        restricted: bool,
184        blockchain_read: BlockchainReadHandle,
185        blockchain_context: BlockchainContextService,
186        blockchain_manager: BlockchainManagerHandle,
187        txpool_read: TxpoolReadHandle,
188        txpool_manager: std::convert::Infallible,
189    ) -> Self {
190        Self {
191            restricted,
192            blockchain_read,
193            blockchain_context,
194            blockchain_manager,
195            txpool_read,
196            txpool_manager,
197        }
198    }
199}
200
201impl RpcHandler for CupratedRpcHandler {
202    fn is_restricted(&self) -> bool {
203        self.restricted
204    }
205}
206
207impl Service<JsonRpcRequest> for CupratedRpcHandler {
208    type Response = JsonRpcResponse;
209    type Error = Error;
210    type Future = BoxFuture<'static, Result<JsonRpcResponse, Error>>;
211
212    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
213        Poll::Ready(Ok(()))
214    }
215
216    fn call(&mut self, request: JsonRpcRequest) -> Self::Future {
217        let state = self.clone();
218        Box::pin(handlers::json_rpc::map_request(state, request))
219    }
220}
221
222impl Service<BinRequest> for CupratedRpcHandler {
223    type Response = BinResponse;
224    type Error = Error;
225    type Future = BoxFuture<'static, Result<BinResponse, Error>>;
226
227    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
228        Poll::Ready(Ok(()))
229    }
230
231    fn call(&mut self, request: BinRequest) -> Self::Future {
232        let state = self.clone();
233        Box::pin(handlers::bin::map_request(state, request))
234    }
235}
236
237impl Service<OtherRequest> for CupratedRpcHandler {
238    type Response = OtherResponse;
239    type Error = Error;
240    type Future = BoxFuture<'static, Result<OtherResponse, Error>>;
241
242    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
243        Poll::Ready(Ok(()))
244    }
245
246    fn call(&mut self, request: OtherRequest) -> Self::Future {
247        let state = self.clone();
248        Box::pin(handlers::other_json::map_request(state, request))
249    }
250}